File indexing completed on 2024-11-10 04:15:55
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 "KDbQuerySchema.h" 0021 #include "KDbQuerySchema_p.h" 0022 #include "KDbQueryAsterisk.h" 0023 #include "KDbConnection.h" 0024 #include "KDbConnection_p.h" 0025 #include "kdb_debug.h" 0026 #include "KDbLookupFieldSchema.h" 0027 #include "KDbOrderByColumn.h" 0028 #include "KDbParser_p.h" 0029 #include "KDbQuerySchemaParameter.h" 0030 #include "KDbRelationship.h" 0031 0032 QString escapeIdentifier(const QString& name, KDbConnection *conn, 0033 KDb::IdentifierEscapingType escapingType) 0034 { 0035 switch (escapingType) { 0036 case KDb::DriverEscaping: 0037 if (conn) 0038 return conn->escapeIdentifier(name); 0039 break; 0040 case KDb::KDbEscaping: 0041 return KDb::escapeIdentifier(name); 0042 } 0043 return QLatin1Char('"') + name + QLatin1Char('"'); 0044 } 0045 0046 KDbQuerySchema::KDbQuerySchema() 0047 : KDbFieldList(false)//fields are not owned by KDbQuerySchema object 0048 , KDbObject(KDb::QueryObjectType) 0049 , d(new KDbQuerySchemaPrivate(this)) 0050 { 0051 } 0052 0053 KDbQuerySchema::KDbQuerySchema(KDbTableSchema *tableSchema) 0054 : KDbFieldList(false)//fields are not owned by KDbQuerySchema object 0055 , KDbObject(KDb::QueryObjectType) 0056 , d(new KDbQuerySchemaPrivate(this)) 0057 { 0058 if (tableSchema) { 0059 d->masterTable = tableSchema; 0060 /*if (!d->masterTable) { 0061 kdbWarning() << "!d->masterTable"; 0062 m_name.clear(); 0063 return; 0064 }*/ 0065 addTable(d->masterTable); 0066 //defaults: 0067 //inherit name from a table 0068 setName(d->masterTable->name()); 0069 //inherit caption from a table 0070 setCaption(d->masterTable->caption()); 0071 0072 // add explicit field list to avoid problems (e.g. with fields added outside of the app): 0073 foreach(KDbField* f, *d->masterTable->fields()) { 0074 addField(f); 0075 } 0076 } 0077 } 0078 0079 KDbQuerySchema::KDbQuerySchema(const KDbQuerySchema& querySchema, KDbConnection *conn) 0080 : KDbFieldList(querySchema, false /* !deepCopyFields */) 0081 , KDbObject(querySchema) 0082 , d(new KDbQuerySchemaPrivate(this, querySchema.d)) 0083 { 0084 //only deep copy query asterisks 0085 for (KDbField* f : qAsConst(*querySchema.fields())) { 0086 KDbField *copiedField; 0087 if (dynamic_cast<KDbQueryAsterisk*>(f)) { 0088 copiedField = f->copy(); 0089 if (static_cast<const KDbFieldList *>(f->parent()) == &querySchema) { 0090 copiedField->setParent(this); 0091 } 0092 } 0093 else { 0094 copiedField = f; 0095 } 0096 addField(copiedField); 0097 } 0098 // this deep copy must be after the 'd' initialization because fieldsExpanded() is used there 0099 d->orderByColumnList = new KDbOrderByColumnList(*querySchema.d->orderByColumnList, conn, 0100 const_cast<KDbQuerySchema*>(&querySchema), this); 0101 } 0102 0103 KDbQuerySchema::~KDbQuerySchema() 0104 { 0105 delete d; 0106 } 0107 0108 void KDbQuerySchema::clear() 0109 { 0110 KDbFieldList::clear(); 0111 KDbObject::clear(); 0112 d->clear(); 0113 } 0114 0115 /*virtual*/ 0116 bool KDbQuerySchema::insertField(int position, KDbField *field) 0117 { 0118 return insertFieldInternal(position, field, -1/*don't bind*/, true); 0119 } 0120 0121 bool KDbQuerySchema::insertInvisibleField(int position, KDbField *field) 0122 { 0123 return insertFieldInternal(position, field, -1/*don't bind*/, false); 0124 } 0125 0126 bool KDbQuerySchema::insertField(int position, KDbField *field, int bindToTable) 0127 { 0128 return insertFieldInternal(position, field, bindToTable, true); 0129 } 0130 0131 bool KDbQuerySchema::insertInvisibleField(int position, KDbField *field, int bindToTable) 0132 { 0133 return insertFieldInternal(position, field, bindToTable, false); 0134 } 0135 0136 bool KDbQuerySchema::insertFieldInternal(int position, KDbField *field, 0137 int bindToTable, bool visible) 0138 { 0139 if (!field) { 0140 kdbWarning() << "!field"; 0141 return false; 0142 } 0143 0144 if (position > fieldCount()) { 0145 kdbWarning() << "position" << position << "out of range"; 0146 return false; 0147 } 0148 if (!field->isQueryAsterisk() && !field->isExpression() && !field->table()) { 0149 kdbWarning() << "field" << field->name() << "must contain table information!"; 0150 return false; 0151 } 0152 if (fieldCount() >= d->visibility.size()) { 0153 d->visibility.resize(d->visibility.size()*2); 0154 d->tablesBoundToColumns.resize(d->tablesBoundToColumns.size()*2); 0155 } 0156 d->clearCachedData(); 0157 if (!KDbFieldList::insertField(position, field)) { 0158 return false; 0159 } 0160 if (field->isQueryAsterisk()) { 0161 d->asterisks.append(field); 0162 //if this is single-table asterisk, 0163 //add a table to list if doesn't exist there: 0164 if (field->table() && !d->tables.contains(field->table())) 0165 d->tables.append(field->table()); 0166 } else if (field->table()) { 0167 //add a table to list if doesn't exist there: 0168 if (!d->tables.contains(field->table())) 0169 d->tables.append(field->table()); 0170 } 0171 //update visibility 0172 //--move bits to make a place for a new one 0173 for (int i = fieldCount() - 1; i > position; i--) 0174 d->visibility.setBit(i, d->visibility.testBit(i - 1)); 0175 d->visibility.setBit(position, visible); 0176 0177 //bind to table 0178 if (bindToTable < -1 || bindToTable > d->tables.count()) { 0179 kdbWarning() << "bindToTable" << bindToTable << "out of range"; 0180 bindToTable = -1; 0181 } 0182 //--move items to make a place for a new one 0183 for (int i = fieldCount() - 1; i > position; i--) 0184 d->tablesBoundToColumns[i] = d->tablesBoundToColumns[i-1]; 0185 d->tablesBoundToColumns[position] = bindToTable; 0186 0187 #ifdef KDB_QUERYSCHEMA_DEBUG 0188 querySchemaDebug() << "bound to table" << bindToTable; 0189 if (bindToTable == -1) 0190 querySchemaDebug() << " <NOT SPECIFIED>"; 0191 else 0192 querySchemaDebug() << " name=" << d->tables.at(bindToTable)->name() 0193 << " alias=" << tableAlias(bindToTable); 0194 QString s; 0195 for (int i = 0; i < fieldCount();i++) 0196 s += (QString::number(d->tablesBoundToColumns[i]) + QLatin1Char(' ')); 0197 querySchemaDebug() << "tablesBoundToColumns == [" << s << "]"; 0198 #endif 0199 0200 if (field->isExpression()) 0201 d->regenerateExprAliases = true; 0202 0203 return true; 0204 } 0205 0206 int KDbQuerySchema::tableBoundToColumn(int columnPosition) const 0207 { 0208 int res = d->tablesBoundToColumns.value(columnPosition, -99); 0209 if (res == -99) { 0210 kdbWarning() << "columnPosition" << columnPosition << "out of range"; 0211 return -1; 0212 } 0213 return res; 0214 } 0215 0216 bool KDbQuerySchema::addField(KDbField* field) 0217 { 0218 return insertField(fieldCount(), field); 0219 } 0220 0221 bool KDbQuerySchema::addField(KDbField* field, int bindToTable) 0222 { 0223 return insertField(fieldCount(), field, bindToTable); 0224 } 0225 0226 bool KDbQuerySchema::addInvisibleField(KDbField* field) 0227 { 0228 return insertInvisibleField(fieldCount(), field); 0229 } 0230 0231 bool KDbQuerySchema::addInvisibleField(KDbField* field, int bindToTable) 0232 { 0233 return insertInvisibleField(fieldCount(), field, bindToTable); 0234 } 0235 0236 bool KDbQuerySchema::removeField(KDbField *field) 0237 { 0238 int indexOfAsterisk = -1; 0239 if (field->isQueryAsterisk()) { 0240 indexOfAsterisk = d->asterisks.indexOf(field); 0241 } 0242 if (!KDbFieldList::removeField(field)) { 0243 return false; 0244 } 0245 d->clearCachedData(); 0246 if (indexOfAsterisk >= 0) { 0247 //querySchemaDebug() << "d->asterisks.removeAt:" << field; 0248 //field->debug(); 0249 d->asterisks.removeAt(indexOfAsterisk); //this will destroy this asterisk 0250 } 0251 //! @todo should we also remove table for this field or asterisk? 0252 return true; 0253 } 0254 0255 bool KDbQuerySchema::addExpressionInternal(const KDbExpression& expr, bool visible) 0256 { 0257 KDbField *field = new KDbField(this, expr); 0258 bool ok; 0259 if (visible) { 0260 ok = addField(field); 0261 } else { 0262 ok = addInvisibleField(field); 0263 } 0264 if (!ok) { 0265 delete field; 0266 } 0267 d->ownedExpressionFields.append(field); 0268 return ok; 0269 } 0270 0271 bool KDbQuerySchema::addExpression(const KDbExpression& expr) 0272 { 0273 return addExpressionInternal(expr, true); 0274 } 0275 0276 bool KDbQuerySchema::addInvisibleExpression(const KDbExpression& expr) 0277 { 0278 return addExpressionInternal(expr, false); 0279 } 0280 0281 bool KDbQuerySchema::isColumnVisible(int position) const 0282 { 0283 return (position < fieldCount()) ? d->visibility.testBit(position) : false; 0284 } 0285 0286 void KDbQuerySchema::setColumnVisible(int position, bool visible) 0287 { 0288 if (position < fieldCount()) 0289 d->visibility.setBit(position, visible); 0290 } 0291 0292 bool KDbQuerySchema::addAsteriskInternal(KDbQueryAsterisk *asterisk, bool visible) 0293 { 0294 if (!asterisk) { 0295 return false; 0296 } 0297 //make unique name 0298 asterisk->setName((asterisk->table() ? (asterisk->table()->name() + QLatin1String(".*")) 0299 : QString(QLatin1Char('*'))) 0300 + QString::number(asterisks()->count())); 0301 return visible ? addField(asterisk) : addInvisibleField(asterisk); 0302 } 0303 0304 bool KDbQuerySchema::addAsterisk(KDbQueryAsterisk *asterisk) 0305 { 0306 return addAsteriskInternal(asterisk, true); 0307 } 0308 0309 bool KDbQuerySchema::addInvisibleAsterisk(KDbQueryAsterisk *asterisk) 0310 { 0311 return addAsteriskInternal(asterisk, false); 0312 } 0313 0314 QDebug operator<<(QDebug dbg, const KDbConnectionAndQuerySchema &connectionAndSchema) 0315 { 0316 KDbConnection* conn = std::get<0>(connectionAndSchema); 0317 const KDbQuerySchema& query = std::get<1>(connectionAndSchema); 0318 //fields 0319 KDbTableSchema *mt = query.masterTable(); 0320 dbg.nospace() << "QUERY"; 0321 dbg.space() << static_cast<const KDbObject&>(query) << '\n'; 0322 dbg.nospace() << " - MASTERTABLE=" << (mt ? mt->name() : QLatin1String("<NULL>")) 0323 << "\n - COLUMNS:\n"; 0324 if (query.fieldCount() > 0) 0325 dbg.nospace() << static_cast<const KDbFieldList&>(query) << '\n'; 0326 else 0327 dbg.nospace() << "<NONE>\n"; 0328 0329 if (query.fieldCount() == 0) 0330 dbg.nospace() << " - NO FIELDS\n"; 0331 else 0332 dbg.nospace() << " - FIELDS EXPANDED ("; 0333 0334 int fieldsExpandedCount = 0; 0335 bool first; 0336 if (query.fieldCount() > 0) { 0337 const KDbQueryColumnInfo::Vector fe(query.fieldsExpanded(conn)); 0338 fieldsExpandedCount = fe.size(); 0339 dbg.nospace() << fieldsExpandedCount << "):\n"; 0340 first = true; 0341 for (int i = 0; i < fieldsExpandedCount; i++) { 0342 KDbQueryColumnInfo *ci = fe[i]; 0343 if (first) 0344 first = false; 0345 else 0346 dbg.nospace() << ",\n"; 0347 dbg.nospace() << *ci; 0348 } 0349 dbg.nospace() << '\n'; 0350 } 0351 0352 //it's safer to delete fieldsExpanded for now 0353 // (debugString() could be called before all fields are added) 0354 0355 //bindings 0356 dbg.nospace() << " - BINDINGS:\n"; 0357 bool bindingsExist = false; 0358 for (int i = 0; i < query.fieldCount(); i++) { 0359 const int tablePos = query.tableBoundToColumn(i); 0360 if (tablePos >= 0) { 0361 const QString tAlias(query.tableAlias(tablePos)); 0362 if (!tAlias.isEmpty()) { 0363 bindingsExist = true; 0364 dbg.space() << "FIELD"; 0365 dbg.space() << static_cast<const KDbFieldList&>(query).field(i)->name(); 0366 dbg.space() << "USES ALIAS"; 0367 dbg.space() << tAlias; 0368 dbg.space() << "OF TABLE"; 0369 dbg.space() << query.tables()->at(tablePos)->name() << '\n'; 0370 } 0371 } 0372 } 0373 if (!bindingsExist) { 0374 dbg.nospace() << "<NONE>\n"; 0375 } 0376 0377 //tables 0378 dbg.nospace() << " - TABLES:\n"; 0379 first = true; 0380 foreach(KDbTableSchema *table, *query.tables()) { 0381 if (first) 0382 first = false; 0383 else 0384 dbg.nospace() << ","; 0385 dbg.space() << table->name(); 0386 } 0387 if (query.tables()->isEmpty()) 0388 dbg.nospace() << "<NONE>"; 0389 0390 //aliases 0391 dbg.nospace() << "\n - COLUMN ALIASES:\n"; 0392 if (query.columnAliasesCount() == 0) { 0393 dbg.nospace() << "<NONE>\n"; 0394 } 0395 else { 0396 int i = -1; 0397 foreach(KDbField *f, *query.fields()) { 0398 i++; 0399 const QString alias(query.columnAlias(i)); 0400 if (!alias.isEmpty()) { 0401 dbg.nospace() << QString::fromLatin1("FIELD #%1:").arg(i); 0402 dbg.space() << (f->name().isEmpty() 0403 ? QLatin1String("<NONAME>") : f->name()) << " -> " << alias << '\n'; 0404 } 0405 } 0406 } 0407 0408 dbg.nospace() << "- TABLE ALIASES:\n"; 0409 if (query.tableAliasesCount() == 0) { 0410 dbg.nospace() << "<NONE>\n"; 0411 } 0412 else { 0413 int i = -1; 0414 foreach(KDbTableSchema* table, *query.tables()) { 0415 i++; 0416 const QString alias(query.tableAlias(i)); 0417 if (!alias.isEmpty()) { 0418 dbg.nospace() << QString::fromLatin1("table #%1:").arg(i); 0419 dbg.space() << (table->name().isEmpty() 0420 ? QLatin1String("<NONAME>") : table->name()) << " -> " << alias << '\n'; 0421 } 0422 } 0423 } 0424 if (!query.whereExpression().isNull()) { 0425 dbg.nospace() << " - WHERE EXPRESSION:\n" << query.whereExpression() << '\n'; 0426 } 0427 dbg.nospace() << qPrintable(QString::fromLatin1(" - ORDER BY (%1):\n").arg(query.orderByColumnList()->count())); 0428 if (query.orderByColumnList()->isEmpty()) { 0429 dbg.nospace() << "<NONE>\n"; 0430 } else { 0431 dbg.nospace() << *query.orderByColumnList(); 0432 } 0433 return dbg.nospace(); 0434 } 0435 0436 KDbTableSchema* KDbQuerySchema::masterTable() const 0437 { 0438 if (d->masterTable) 0439 return d->masterTable; 0440 if (d->tables.isEmpty()) 0441 return nullptr; 0442 0443 //try to find master table if there's only one table (with possible aliasses) 0444 QString tableNameLower; 0445 int num = -1; 0446 foreach(KDbTableSchema *table, d->tables) { 0447 num++; 0448 if (!tableNameLower.isEmpty() && table->name().toLower() != tableNameLower) { 0449 //two or more different tables 0450 return nullptr; 0451 } 0452 tableNameLower = tableAlias(num); 0453 } 0454 return d->tables.first(); 0455 } 0456 0457 void KDbQuerySchema::setMasterTable(KDbTableSchema *table) 0458 { 0459 if (table) 0460 d->masterTable = table; 0461 } 0462 0463 QList<KDbTableSchema*>* KDbQuerySchema::tables() const 0464 { 0465 return &d->tables; 0466 } 0467 0468 void KDbQuerySchema::addTable(KDbTableSchema *table, const QString& alias) 0469 { 0470 querySchemaDebug() << (void *)table << "alias=" << alias; 0471 if (!table) 0472 return; 0473 0474 // only append table if: it has alias or it has no alias but there is no such table on the list 0475 if (alias.isEmpty() && d->tables.contains(table)) { 0476 int num = -1; 0477 foreach(KDbTableSchema *t, d->tables) { 0478 num++; 0479 if (0 == t->name().compare(table->name(), Qt::CaseInsensitive)) { 0480 if (tableAlias(num).isEmpty()) { 0481 querySchemaDebug() << "table" << table->name() << "without alias already added"; 0482 return; 0483 } 0484 } 0485 } 0486 } 0487 d->tables.append(table); 0488 if (!alias.isEmpty()) 0489 setTableAlias(d->tables.count() - 1, alias); 0490 } 0491 0492 void KDbQuerySchema::removeTable(KDbTableSchema *table) 0493 { 0494 if (!table) 0495 return; 0496 if (d->masterTable == table) 0497 d->masterTable = nullptr; 0498 d->tables.removeAt(d->tables.indexOf(table)); 0499 //! @todo remove fields! 0500 } 0501 0502 KDbTableSchema* KDbQuerySchema::table(const QString& tableName) const 0503 { 0504 //! @todo maybe use tables_byname? 0505 foreach(KDbTableSchema *table, d->tables) { 0506 if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) { 0507 return table; 0508 } 0509 } 0510 return nullptr; 0511 } 0512 0513 bool KDbQuerySchema::contains(KDbTableSchema *table) const 0514 { 0515 return d->tables.contains(table); 0516 } 0517 0518 KDbField* KDbQuerySchema::findTableField(const QString &fieldOrTableAndFieldName) const 0519 { 0520 QString tableName, fieldName; 0521 if (!KDb::splitToTableAndFieldParts(fieldOrTableAndFieldName, 0522 &tableName, &fieldName, 0523 KDb::SetFieldNameIfNoTableName)) { 0524 return nullptr; 0525 } 0526 if (tableName.isEmpty()) { 0527 foreach(KDbTableSchema *table, d->tables) { 0528 if (table->field(fieldName)) 0529 return table->field(fieldName); 0530 } 0531 return nullptr; 0532 } 0533 KDbTableSchema *tableSchema = table(tableName); 0534 if (!tableSchema) 0535 return nullptr; 0536 return tableSchema->field(fieldName); 0537 } 0538 0539 int KDbQuerySchema::columnAliasesCount() const 0540 { 0541 return d->columnAliasesCount(); 0542 } 0543 0544 QString KDbQuerySchema::columnAlias(int position) const 0545 { 0546 return d->columnAlias(position); 0547 } 0548 0549 bool KDbQuerySchema::hasColumnAlias(int position) const 0550 { 0551 return d->hasColumnAlias(position); 0552 } 0553 0554 bool KDbQuerySchema::setColumnAlias(int position, const QString& alias) 0555 { 0556 if (position >= fieldCount()) { 0557 kdbWarning() << "position" << position << "out of range!"; 0558 return false; 0559 } 0560 const QString fixedAlias(alias.trimmed()); 0561 KDbField *f = KDbFieldList::field(position); 0562 if (f->captionOrName().isEmpty() && fixedAlias.isEmpty()) { 0563 kdbWarning() << "position" << position << "could not remove alias when no name is specified for expression column!"; 0564 return false; 0565 } 0566 return d->setColumnAlias(position, fixedAlias); 0567 } 0568 0569 int KDbQuerySchema::tableAliasesCount() const 0570 { 0571 return d->tableAliases.count(); 0572 } 0573 0574 QString KDbQuerySchema::tableAlias(int position) const 0575 { 0576 return d->tableAliases.value(position); 0577 } 0578 0579 QString KDbQuerySchema::tableAlias(const QString& tableName) const 0580 { 0581 const int pos = tablePosition(tableName); 0582 if (pos == -1) { 0583 return QString(); 0584 } 0585 return d->tableAliases.value(pos); 0586 } 0587 0588 QString KDbQuerySchema::tableAliasOrName(const QString& tableName) const 0589 { 0590 const int pos = tablePosition(tableName); 0591 if (pos == -1) { 0592 return QString(); 0593 } 0594 return KDb::iifNotEmpty(d->tableAliases.value(pos), tableName); 0595 } 0596 0597 int KDbQuerySchema::tablePositionForAlias(const QString& name) const 0598 { 0599 return d->tablePositionForAlias(name); 0600 } 0601 0602 int KDbQuerySchema::tablePosition(const QString& tableName) const 0603 { 0604 int num = -1; 0605 foreach(KDbTableSchema* table, d->tables) { 0606 num++; 0607 if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) { 0608 return num; 0609 } 0610 } 0611 return -1; 0612 } 0613 0614 QList<int> KDbQuerySchema::tablePositions(const QString& tableName) const 0615 { 0616 QList<int> result; 0617 int num = -1; 0618 foreach(KDbTableSchema* table, d->tables) { 0619 num++; 0620 if (0 == table->name().compare(tableName, Qt::CaseInsensitive)) { 0621 result += num; 0622 } 0623 } 0624 return result; 0625 } 0626 0627 bool KDbQuerySchema::hasTableAlias(int position) const 0628 { 0629 return d->tableAliases.contains(position); 0630 } 0631 0632 bool KDbQuerySchema::hasTableAlias(const QString &name) const 0633 { 0634 return d->tablePositionForAlias(name) != -1; 0635 } 0636 0637 int KDbQuerySchema::columnPositionForAlias(const QString& name) const 0638 { 0639 return d->columnPositionForAlias(name); 0640 } 0641 0642 bool KDbQuerySchema::hasColumnAlias(const QString &name) const 0643 { 0644 return d->columnPositionForAlias(name) != -1; 0645 } 0646 0647 bool KDbQuerySchema::setTableAlias(int position, const QString& alias) 0648 { 0649 if (position >= d->tables.count()) { 0650 kdbWarning() << "position" << position << "out of range!"; 0651 return false; 0652 } 0653 const QString fixedAlias(alias.trimmed()); 0654 if (fixedAlias.isEmpty()) { 0655 const QString oldAlias(d->tableAliases.take(position)); 0656 if (!oldAlias.isEmpty()) { 0657 d->removeTablePositionForAlias(oldAlias); 0658 } 0659 return true; 0660 } 0661 return d->setTableAlias(position, fixedAlias); 0662 } 0663 0664 QList<KDbRelationship*>* KDbQuerySchema::relationships() const 0665 { 0666 return &d->relations; 0667 } 0668 0669 KDbField::List* KDbQuerySchema::asterisks() const 0670 { 0671 return &d->asterisks; 0672 } 0673 0674 KDbEscapedString KDbQuerySchema::statement() const 0675 { 0676 return d->sql; 0677 } 0678 0679 void KDbQuerySchema::setStatement(const KDbEscapedString& sql) 0680 { 0681 d->sql = sql; 0682 } 0683 0684 const KDbField* KDbQuerySchema::field(KDbConnection *conn, const QString& identifier, 0685 ExpandMode mode) const 0686 { 0687 KDbQueryColumnInfo *ci = columnInfo(conn, identifier, mode); 0688 return ci ? ci->field() : nullptr; 0689 } 0690 0691 KDbField* KDbQuerySchema::field(KDbConnection *conn, const QString& identifier, ExpandMode mode) 0692 { 0693 return const_cast<KDbField *>( 0694 static_cast<const KDbQuerySchema *>(this)->field(conn, identifier, mode)); 0695 } 0696 0697 KDbField* KDbQuerySchema::field(int id) 0698 { 0699 return KDbFieldList::field(id); 0700 } 0701 0702 const KDbField* KDbQuerySchema::field(int id) const 0703 { 0704 return KDbFieldList::field(id); 0705 } 0706 0707 KDbQueryColumnInfo *KDbQuerySchema::columnInfo(KDbConnection *conn, const QString &identifier, 0708 ExpandMode mode) const 0709 { 0710 const KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn); 0711 return mode == ExpandMode::Expanded ? cache->columnInfosByNameExpanded.value(identifier) 0712 : cache->columnInfosByName.value(identifier); 0713 } 0714 0715 KDbQueryColumnInfo::Vector KDbQuerySchema::fieldsExpandedInternal( 0716 KDbConnection *conn, FieldsExpandedMode mode, bool onlyVisible) const 0717 { 0718 if (!conn) { 0719 kdbWarning() << "Connection required"; 0720 return KDbQueryColumnInfo::Vector(); 0721 } 0722 KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn); 0723 const KDbQueryColumnInfo::Vector *realFieldsExpanded 0724 = onlyVisible ? &cache->visibleFieldsExpanded : &cache->fieldsExpanded; 0725 if (mode == FieldsExpandedMode::WithInternalFields 0726 || mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) 0727 { 0728 //a ref to a proper pointer (as we cache the vector for two cases) 0729 KDbQueryColumnInfo::Vector& tmpFieldsExpandedWithInternal = 0730 (mode == FieldsExpandedMode::WithInternalFields) ? 0731 (onlyVisible ? cache->visibleFieldsExpandedWithInternal : cache->fieldsExpandedWithInternal) 0732 : (onlyVisible ? cache->visibleFieldsExpandedWithInternalAndRecordId : cache->fieldsExpandedWithInternalAndRecordId); 0733 //special case 0734 if (tmpFieldsExpandedWithInternal.isEmpty()) { 0735 //glue expanded and internal fields and cache it 0736 const int internalFieldCount = cache->internalFields.size(); 0737 const int fieldsExpandedVectorSize = realFieldsExpanded->size(); 0738 const int size = fieldsExpandedVectorSize + internalFieldCount 0739 + ((mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) ? 1 : 0) /*ROWID*/; 0740 tmpFieldsExpandedWithInternal.resize(size); 0741 for (int i = 0; i < fieldsExpandedVectorSize; ++i) { 0742 tmpFieldsExpandedWithInternal[i] = realFieldsExpanded->at(i); 0743 } 0744 if (internalFieldCount > 0) { 0745 for (int i = 0; i < internalFieldCount; ++i) { 0746 KDbQueryColumnInfo *info = cache->internalFields[i]; 0747 tmpFieldsExpandedWithInternal[fieldsExpandedVectorSize + i] = info; 0748 } 0749 } 0750 if (mode == FieldsExpandedMode::WithInternalFieldsAndRecordId) { 0751 if (!d->fakeRecordIdField) { 0752 d->fakeRecordIdField = new KDbField(QLatin1String("rowID"), KDbField::BigInteger); 0753 d->fakeRecordIdCol = new KDbQueryColumnInfo(d->fakeRecordIdField, QString(), true); 0754 d->fakeRecordIdCol->d->querySchema = this; 0755 d->fakeRecordIdCol->d->connection = conn; 0756 } 0757 tmpFieldsExpandedWithInternal[fieldsExpandedVectorSize + internalFieldCount] = d->fakeRecordIdCol; 0758 } 0759 } 0760 return tmpFieldsExpandedWithInternal; 0761 } 0762 0763 if (mode == FieldsExpandedMode::Default) { 0764 return *realFieldsExpanded; 0765 } 0766 0767 //mode == Unique: 0768 QSet<QString> columnsAlreadyFound; 0769 const int fieldsExpandedCount(realFieldsExpanded->count()); 0770 KDbQueryColumnInfo::Vector result(fieldsExpandedCount); //initial size is set 0771 //compute unique list 0772 int uniqueListCount = 0; 0773 for (int i = 0; i < fieldsExpandedCount; i++) { 0774 KDbQueryColumnInfo *ci = realFieldsExpanded->at(i); 0775 if (!columnsAlreadyFound.contains(ci->aliasOrName())) { 0776 columnsAlreadyFound.insert(ci->aliasOrName()); 0777 result[uniqueListCount++] = ci; 0778 } 0779 } 0780 result.resize(uniqueListCount); //update result size 0781 return result; 0782 } 0783 0784 KDbQueryColumnInfo::Vector KDbQuerySchema::internalFields(KDbConnection *conn) const 0785 { 0786 KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn); 0787 return cache->internalFields; 0788 } 0789 0790 KDbQueryColumnInfo* KDbQuerySchema::expandedOrInternalField(KDbConnection *conn, int index) const 0791 { 0792 return fieldsExpanded(conn, FieldsExpandedMode::WithInternalFields).value(index); 0793 } 0794 0795 inline static QString lookupColumnKey(KDbField *foreignField, KDbField* field) 0796 { 0797 QString res; 0798 if (field->table()) // can be 0 for anonymous fields built as joined multiple visible columns 0799 res = field->table()->name() + QLatin1Char('.'); 0800 return res + field->name() + QLatin1Char('_') + foreignField->table()->name() 0801 + QLatin1Char('.') + foreignField->name(); 0802 } 0803 0804 KDbQuerySchemaFieldsExpanded *KDbQuerySchema::computeFieldsExpanded(KDbConnection *conn) const 0805 { 0806 KDbQuerySchemaFieldsExpanded *cache = conn->d->fieldsExpanded(this); 0807 if (cache) { 0808 return cache; 0809 } 0810 cache = new KDbQuerySchemaFieldsExpanded; 0811 QScopedPointer<KDbQuerySchemaFieldsExpanded> guard(cache); 0812 0813 //collect all fields in a list (not a vector yet, because we do not know its size) 0814 KDbQueryColumnInfo::List list; //temporary 0815 KDbQueryColumnInfo::List lookup_list; //temporary, for collecting additional fields related to lookup fields 0816 QHash<KDbQueryColumnInfo*, bool> columnInfosOutsideAsterisks; //helper for filling d->columnInfosByName 0817 int i = 0; 0818 int numberOfColumnsWithMultipleVisibleFields = 0; //used to find an unique name for anonymous field 0819 int fieldPosition = -1; 0820 for (KDbField *f : *fields()) { 0821 fieldPosition++; 0822 if (f->isQueryAsterisk()) { 0823 if (static_cast<KDbQueryAsterisk*>(f)->isSingleTableAsterisk()) { 0824 const KDbField::List *ast_fields = static_cast<KDbQueryAsterisk*>(f)->table()->fields(); 0825 foreach(KDbField *ast_f, *ast_fields) { 0826 KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(ast_f, QString()/*no field for asterisk!*/, 0827 isColumnVisible(fieldPosition)); 0828 ci->d->querySchema = this; 0829 ci->d->connection = conn; 0830 list.append(ci); 0831 querySchemaDebug() << "caching (unexpanded) columns order:" << *ci 0832 << "at position" << fieldPosition; 0833 cache->columnsOrder.insert(ci, fieldPosition); 0834 } 0835 } else {//all-tables asterisk: iterate through table list 0836 foreach(KDbTableSchema *table, d->tables) { 0837 //add all fields from this table 0838 const KDbField::List *tab_fields = table->fields(); 0839 foreach(KDbField *tab_f, *tab_fields) { 0840 //! @todo (js): perhaps not all fields should be appended here 0841 // d->detailedVisibility += isFieldVisible(fieldPosition); 0842 // list.append(tab_f); 0843 KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(tab_f, QString()/*no field for asterisk!*/, 0844 isColumnVisible(fieldPosition)); 0845 ci->d->querySchema = this; 0846 ci->d->connection = conn; 0847 list.append(ci); 0848 querySchemaDebug() << "caching (unexpanded) columns order:" << *ci 0849 << "at position" << fieldPosition; 0850 cache->columnsOrder.insert(ci, fieldPosition); 0851 } 0852 } 0853 } 0854 } else { 0855 //a single field 0856 KDbQueryColumnInfo *ci = new KDbQueryColumnInfo(f, columnAlias(fieldPosition), isColumnVisible(fieldPosition)); 0857 ci->d->querySchema = this; 0858 ci->d->connection = conn; 0859 list.append(ci); 0860 columnInfosOutsideAsterisks.insert(ci, true); 0861 querySchemaDebug() << "caching (unexpanded) column's order:" << *ci << "at position" 0862 << fieldPosition; 0863 cache->columnsOrder.insert(ci, fieldPosition); 0864 cache->columnsOrderWithoutAsterisks.insert(ci, fieldPosition); 0865 0866 //handle lookup field schema 0867 KDbLookupFieldSchema *lookupFieldSchema = f->table() ? f->table()->lookupFieldSchema(*f) : nullptr; 0868 if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0) 0869 continue; 0870 // Lookup field schema found: 0871 // Now we also need to fetch "visible" value from the lookup table, not only the value of binding. 0872 // -> build LEFT OUTER JOIN clause for this purpose (LEFT, not INNER because the binding can be broken) 0873 // "LEFT OUTER JOIN lookupTable ON thisTable.thisField=lookupTable.boundField" 0874 KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource(); 0875 if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Table) { 0876 KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name()); 0877 KDbFieldList* visibleColumns = nullptr; 0878 KDbField *boundField = nullptr; 0879 if (lookupTable 0880 && lookupFieldSchema->boundColumn() < lookupTable->fieldCount() 0881 && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns())) 0882 && (boundField = lookupTable->field(lookupFieldSchema->boundColumn()))) { 0883 KDbField *visibleColumn = nullptr; 0884 // for single visible column, just add it as-is 0885 if (visibleColumns->fieldCount() == 1) { 0886 visibleColumn = visibleColumns->fields()->first(); 0887 } else { 0888 // for multiple visible columns, build an expression column 0889 // (the expression object will be owned by column info) 0890 visibleColumn = new KDbField(); 0891 visibleColumn->setName( 0892 QString::fromLatin1("[multiple_visible_fields_%1]") 0893 .arg(++numberOfColumnsWithMultipleVisibleFields)); 0894 visibleColumn->setExpression( 0895 KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant()/*not important*/)); 0896 cache->ownedVisibleFields.append(visibleColumn); // remember to delete later 0897 } 0898 0899 KDbQueryColumnInfo *lookupCi = new KDbQueryColumnInfo( 0900 visibleColumn, QString(), true /*visible*/, ci /*foreign*/); 0901 lookupCi->d->querySchema = this; 0902 lookupCi->d->connection = conn; 0903 lookup_list.append(lookupCi); 0904 /* 0905 //add visibleField to the list of SELECTed fields if it is not yes present there 0906 if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) { 0907 if (!table( visibleField->table()->name() )) { 0908 } 0909 if (!sql.isEmpty()) 0910 sql += QString::fromLatin1(", "); 0911 sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "." 0912 + escapeIdentifier(visibleField->name(), drvEscaping)); 0913 }*/ 0914 } 0915 delete visibleColumns; 0916 } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Query) { 0917 KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name()); 0918 if (!lookupQuery) 0919 continue; 0920 const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded( 0921 lookupQuery->fieldsExpanded(conn)); 0922 if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) 0923 continue; 0924 KDbQueryColumnInfo *boundColumnInfo = nullptr; 0925 if (!(boundColumnInfo = lookupQueryFieldsExpanded.value(lookupFieldSchema->boundColumn()))) 0926 continue; 0927 KDbField *boundField = boundColumnInfo->field(); 0928 if (!boundField) 0929 continue; 0930 const QList<int> visibleColumns(lookupFieldSchema->visibleColumns()); 0931 bool ok = true; 0932 // all indices in visibleColumns should be in [0..lookupQueryFieldsExpanded.size()-1] 0933 foreach(int visibleColumn, visibleColumns) { 0934 if (visibleColumn >= lookupQueryFieldsExpanded.count()) { 0935 ok = false; 0936 break; 0937 } 0938 } 0939 if (!ok) 0940 continue; 0941 KDbField *visibleColumn = nullptr; 0942 // for single visible column, just add it as-is 0943 if (visibleColumns.count() == 1) { 0944 visibleColumn = lookupQueryFieldsExpanded.value(visibleColumns.first())->field(); 0945 } else { 0946 // for multiple visible columns, build an expression column 0947 // (the expression object will be owned by column info) 0948 visibleColumn = new KDbField(); 0949 visibleColumn->setName( 0950 QString::fromLatin1("[multiple_visible_fields_%1]") 0951 .arg(++numberOfColumnsWithMultipleVisibleFields)); 0952 visibleColumn->setExpression( 0953 KDbConstExpression(KDbToken::CHARACTER_STRING_LITERAL, QVariant()/*not important*/)); 0954 cache->ownedVisibleFields.append(visibleColumn); // remember to delete later 0955 } 0956 0957 KDbQueryColumnInfo *lookupCi = new KDbQueryColumnInfo( 0958 visibleColumn, QString(), true /*visible*/, ci /*foreign*/); 0959 lookupCi->d->querySchema = this; 0960 lookupCi->d->connection = conn; 0961 lookup_list.append(lookupCi); 0962 /* 0963 //add visibleField to the list of SELECTed fields if it is not yes present there 0964 if (!findTableField( visibleField->table()->name()+"."+visibleField->name() )) { 0965 if (!table( visibleField->table()->name() )) { 0966 } 0967 if (!sql.isEmpty()) 0968 sql += QString::fromLatin1(", "); 0969 sql += (escapeIdentifier(visibleField->table()->name(), drvEscaping) + "." 0970 + escapeIdentifier(visibleField->name(), drvEscaping)); 0971 }*/ 0972 } 0973 } 0974 } 0975 //prepare clean vector for expanded list, and a map for order information 0976 cache->fieldsExpanded.resize(list.count()); 0977 cache->visibleFieldsExpanded.resize(list.count()); 0978 0979 /*fill (based on prepared 'list' and 'lookup_list'): 0980 -the vector 0981 -the map 0982 -"fields by name" dictionary 0983 */ 0984 i = -1; 0985 int visibleIndex = -1; 0986 foreach(KDbQueryColumnInfo* ci, list) { 0987 i++; 0988 cache->fieldsExpanded[i] = ci; 0989 if (ci->isVisible()) { 0990 ++visibleIndex; 0991 cache->visibleFieldsExpanded[visibleIndex] = ci; 0992 } 0993 cache->columnsOrderExpanded.insert(ci, i); 0994 //remember field by name/alias/table.name if there's no such string yet in d->columnInfosByNameExpanded 0995 if (!ci->alias().isEmpty()) { 0996 //store alias and table.alias 0997 if (!cache->columnInfosByNameExpanded.contains(ci->alias())) { 0998 cache->columnInfosByNameExpanded.insert(ci->alias(), ci); 0999 } 1000 QString tableAndAlias(ci->alias()); 1001 if (ci->field()->table()) 1002 tableAndAlias.prepend(ci->field()->table()->name() + QLatin1Char('.')); 1003 if (!cache->columnInfosByNameExpanded.contains(tableAndAlias)) { 1004 cache->columnInfosByNameExpanded.insert(tableAndAlias, ci); 1005 } 1006 //the same for "unexpanded" list 1007 if (columnInfosOutsideAsterisks.contains(ci)) { 1008 if (!cache->columnInfosByName.contains(ci->alias())) { 1009 cache->columnInfosByName.insert(ci->alias(), ci); 1010 } 1011 if (!cache->columnInfosByName.contains(tableAndAlias)) { 1012 cache->columnInfosByName.insert(tableAndAlias, ci); 1013 } 1014 } 1015 } else { 1016 //no alias: store name and table.name 1017 if (!cache->columnInfosByNameExpanded.contains(ci->field()->name())) { 1018 cache->columnInfosByNameExpanded.insert(ci->field()->name(), ci); 1019 } 1020 QString tableAndName(ci->field()->name()); 1021 if (ci->field()->table()) 1022 tableAndName.prepend(ci->field()->table()->name() + QLatin1Char('.')); 1023 if (!cache->columnInfosByNameExpanded.contains(tableAndName)) { 1024 cache->columnInfosByNameExpanded.insert(tableAndName, ci); 1025 } 1026 //the same for "unexpanded" list 1027 if (columnInfosOutsideAsterisks.contains(ci)) { 1028 if (!cache->columnInfosByName.contains(ci->field()->name())) { 1029 cache->columnInfosByName.insert(ci->field()->name(), ci); 1030 } 1031 if (!cache->columnInfosByName.contains(tableAndName)) { 1032 cache->columnInfosByName.insert(tableAndName, ci); 1033 } 1034 } 1035 } 1036 } 1037 cache->visibleFieldsExpanded.resize(visibleIndex + 1); 1038 1039 //remove duplicates for lookup fields 1040 QHash<QString, int> lookup_dict; //used to fight duplicates and to update KDbQueryColumnInfo::indexForVisibleLookupValue() 1041 // (a mapping from table.name string to int* lookupFieldIndex 1042 i = 0; 1043 for (QMutableListIterator<KDbQueryColumnInfo*> it(lookup_list); it.hasNext();) { 1044 KDbQueryColumnInfo* ci = it.next(); 1045 const QString key(lookupColumnKey(ci->foreignColumn()->field(), ci->field())); 1046 if (lookup_dict.contains(key)) { 1047 // this table.field is already fetched by this query 1048 it.remove(); 1049 delete ci; 1050 } else { 1051 lookup_dict.insert(key, i); 1052 i++; 1053 } 1054 } 1055 1056 //create internal expanded list with lookup fields 1057 cache->internalFields.resize(lookup_list.count()); 1058 i = -1; 1059 foreach(KDbQueryColumnInfo *ci, lookup_list) { 1060 i++; 1061 //add it to the internal list 1062 cache->internalFields[i] = ci; 1063 cache->columnsOrderExpanded.insert(ci, list.count() + i); 1064 } 1065 1066 //update KDbQueryColumnInfo::indexForVisibleLookupValue() cache for columns 1067 numberOfColumnsWithMultipleVisibleFields = 0; 1068 for (i = 0; i < cache->fieldsExpanded.size(); i++) { 1069 KDbQueryColumnInfo* ci = cache->fieldsExpanded[i]; 1070 //! @todo KDbQuerySchema itself will also support lookup fields... 1071 KDbLookupFieldSchema *lookupFieldSchema 1072 = ci->field()->table() ? ci->field()->table()->lookupFieldSchema(*ci->field()) : nullptr; 1073 if (!lookupFieldSchema || lookupFieldSchema->boundColumn() < 0) 1074 continue; 1075 const KDbLookupFieldSchemaRecordSource recordSource = lookupFieldSchema->recordSource(); 1076 if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Table) { 1077 KDbTableSchema *lookupTable = conn->tableSchema(recordSource.name()); 1078 KDbFieldList* visibleColumns = nullptr; 1079 if (lookupTable 1080 && lookupFieldSchema->boundColumn() < lookupTable->fieldCount() 1081 && (visibleColumns = lookupTable->subList(lookupFieldSchema->visibleColumns()))) { 1082 // for single visible column, just add it as-is 1083 if (visibleColumns->fieldCount() == 1) { 1084 KDbField *visibleColumn = visibleColumns->fields()->first(); 1085 const QString key(lookupColumnKey(ci->field(), visibleColumn)); 1086 int index = lookup_dict.value(key, -99); 1087 if (index != -99) 1088 ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index); 1089 } else { 1090 const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3") 1091 .arg(++numberOfColumnsWithMultipleVisibleFields) 1092 .arg(ci->field()->table()->name(), ci->field()->name())); 1093 int index = lookup_dict.value(key, -99); 1094 if (index != -99) 1095 ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index); 1096 } 1097 } 1098 delete visibleColumns; 1099 } else if (recordSource.type() == KDbLookupFieldSchemaRecordSource::Type::Query) { 1100 KDbQuerySchema *lookupQuery = conn->querySchema(recordSource.name()); 1101 if (!lookupQuery) 1102 continue; 1103 const KDbQueryColumnInfo::Vector lookupQueryFieldsExpanded( 1104 lookupQuery->fieldsExpanded(conn)); 1105 if (lookupFieldSchema->boundColumn() >= lookupQueryFieldsExpanded.count()) 1106 continue; 1107 KDbQueryColumnInfo *boundColumnInfo = nullptr; 1108 if (!(boundColumnInfo = lookupQueryFieldsExpanded.value(lookupFieldSchema->boundColumn()))) 1109 continue; 1110 KDbField *boundField = boundColumnInfo->field(); 1111 if (!boundField) 1112 continue; 1113 const QList<int> visibleColumns(lookupFieldSchema->visibleColumns()); 1114 // for single visible column, just add it as-is 1115 if (visibleColumns.count() == 1) { 1116 if (lookupQueryFieldsExpanded.count() > visibleColumns.first()) { // sanity check 1117 KDbField *visibleColumn = lookupQueryFieldsExpanded.at(visibleColumns.first())->field(); 1118 const QString key(lookupColumnKey(ci->field(), visibleColumn)); 1119 int index = lookup_dict.value(key, -99); 1120 if (index != -99) 1121 ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index); 1122 } 1123 } else { 1124 const QString key(QString::fromLatin1("[multiple_visible_fields_%1]_%2.%3") 1125 .arg(++numberOfColumnsWithMultipleVisibleFields) 1126 .arg(ci->field()->table()->name(), ci->field()->name())); 1127 int index = lookup_dict.value(key, -99); 1128 if (index != -99) 1129 ci->setIndexForVisibleLookupValue(cache->fieldsExpanded.size() + index); 1130 } 1131 } else { 1132 kdbWarning() << "unsupported record source type" << recordSource.typeName(); 1133 } 1134 } 1135 if (d->recentConnection != conn) { 1136 if (d->recentConnection) { 1137 // connection changed: remove old cache 1138 d->recentConnection->d->removeFieldsExpanded(this); 1139 } 1140 d->recentConnection = conn; 1141 } 1142 conn->d->insertFieldsExpanded(this, guard.take()); 1143 return cache; 1144 } 1145 1146 QHash<KDbQueryColumnInfo*, int> KDbQuerySchema::columnsOrder(KDbConnection *conn, 1147 ColumnsOrderMode mode) const 1148 { 1149 KDbQuerySchemaFieldsExpanded *cache = computeFieldsExpanded(conn); 1150 if (mode == ColumnsOrderMode::UnexpandedList) { 1151 return cache->columnsOrder; 1152 } else if (mode == ColumnsOrderMode::UnexpandedListWithoutAsterisks) { 1153 return cache->columnsOrderWithoutAsterisks; 1154 } 1155 return cache->columnsOrderExpanded; 1156 } 1157 1158 QVector<int> KDbQuerySchema::pkeyFieldsOrder(KDbConnection *conn) const 1159 { 1160 if (d->pkeyFieldsOrder) 1161 return *d->pkeyFieldsOrder; 1162 1163 KDbTableSchema *tbl = masterTable(); 1164 if (!tbl || !tbl->primaryKey()) 1165 return QVector<int>(); 1166 1167 //get order of PKEY fields (e.g. for records updating or inserting ) 1168 KDbIndexSchema *pkey = tbl->primaryKey(); 1169 querySchemaDebug() << *pkey; 1170 d->pkeyFieldsOrder = new QVector<int>(pkey->fieldCount(), -1); 1171 1172 d->pkeyFieldCount = 0; 1173 const KDbQueryColumnInfo::Vector fieldsExpanded(this->fieldsExpanded(conn)); 1174 const int fCount = fieldsExpanded.count(); 1175 for (int i = 0; i < fCount; i++) { 1176 const KDbQueryColumnInfo *fi = fieldsExpanded[i]; 1177 const int fieldIndex = fi->field()->table() == tbl ? pkey->indexOf(*fi->field()) : -1; 1178 if (fieldIndex != -1 /* field found in PK */ 1179 && d->pkeyFieldsOrder->at(fieldIndex) == -1 /* first time */) 1180 { 1181 querySchemaDebug() << "FIELD" << fi->field()->name() << "IS IN PKEY AT POSITION #" 1182 << fieldIndex; 1183 (*d->pkeyFieldsOrder)[fieldIndex] = i; 1184 d->pkeyFieldCount++; 1185 } 1186 } 1187 querySchemaDebug() << d->pkeyFieldCount << " OUT OF " << pkey->fieldCount() 1188 << " PKEY'S FIELDS FOUND IN QUERY " << name(); 1189 return *d->pkeyFieldsOrder; 1190 } 1191 1192 int KDbQuerySchema::pkeyFieldCount(KDbConnection *conn) 1193 { 1194 (void)pkeyFieldsOrder(conn); /* rebuild information */ 1195 return d->pkeyFieldCount; 1196 } 1197 1198 KDbRelationship* KDbQuerySchema::addRelationship(KDbField *field1, KDbField *field2) 1199 { 1200 //@todo: find existing global db relationships 1201 KDbRelationship *r = new KDbRelationship(this, field1, field2); 1202 if (r->isEmpty()) { 1203 delete r; 1204 return nullptr; 1205 } 1206 1207 d->relations.append(r); 1208 return r; 1209 } 1210 1211 KDbQueryColumnInfo::List* KDbQuerySchema::autoIncrementFields(KDbConnection *conn) const 1212 { 1213 if (!d->autoincFields) { 1214 d->autoincFields = new KDbQueryColumnInfo::List(); 1215 } 1216 KDbTableSchema *mt = masterTable(); 1217 if (!mt) { 1218 kdbWarning() << "no master table!"; 1219 return d->autoincFields; 1220 } 1221 if (d->autoincFields->isEmpty()) {//no cache 1222 const KDbQueryColumnInfo::Vector fieldsExpanded(this->fieldsExpanded(conn)); 1223 for (int i = 0; i < fieldsExpanded.count(); i++) { 1224 KDbQueryColumnInfo *ci = fieldsExpanded[i]; 1225 if (ci->field()->table() == mt && ci->field()->isAutoIncrement()) { 1226 d->autoincFields->append(ci); 1227 } 1228 } 1229 } 1230 return d->autoincFields; 1231 } 1232 1233 // static 1234 KDbEscapedString KDbQuerySchema::sqlColumnsList(const KDbQueryColumnInfo::List &infolist, 1235 KDbConnection *conn, 1236 KDb::IdentifierEscapingType escapingType) 1237 { 1238 KDbEscapedString result; 1239 result.reserve(256); 1240 bool start = true; 1241 foreach(KDbQueryColumnInfo* ci, infolist) { 1242 if (!start) 1243 result += ","; 1244 else 1245 start = false; 1246 result += escapeIdentifier(ci->field()->name(), conn, escapingType); 1247 } 1248 return result; 1249 } 1250 1251 KDbEscapedString KDbQuerySchema::autoIncrementSqlFieldsList(KDbConnection *conn) const 1252 { 1253 // QWeakPointer<const KDbDriver> driverWeakPointer 1254 // = DriverManagerInternal::self()->driverWeakPointer(*conn->driver()); 1255 if ( /*d->lastUsedDriverForAutoIncrementSQLFieldsList != driverWeakPointer 1256 ||*/ d->autoIncrementSqlFieldsList.isEmpty()) 1257 { 1258 d->autoIncrementSqlFieldsList = KDbQuerySchema::sqlColumnsList(*autoIncrementFields(conn), conn); 1259 //d->lastUsedDriverForAutoIncrementSQLFieldsList = driverWeakPointer; 1260 } 1261 return d->autoIncrementSqlFieldsList; 1262 } 1263 1264 static void setResult(const KDbParseInfoInternal &parseInfo, 1265 QString *errorMessage, QString *errorDescription) 1266 { 1267 if (errorMessage) { 1268 *errorMessage = parseInfo.errorMessage(); 1269 } 1270 if (errorDescription) { 1271 *errorDescription = parseInfo.errorDescription(); 1272 } 1273 } 1274 1275 bool KDbQuerySchema::setWhereExpression(const KDbExpression &expr, QString *errorMessage, 1276 QString *errorDescription) 1277 { 1278 KDbExpression newWhereExpr = expr.clone(); 1279 KDbParseInfoInternal parseInfo(this); 1280 QString tempErrorMessage; 1281 QString tempErrorDescription; 1282 QString *errorMessagePointer = errorMessage ? errorMessage : &tempErrorMessage; 1283 QString *errorDescriptionPointer 1284 = errorDescription ? errorDescription : &tempErrorDescription; 1285 if (!newWhereExpr.validate(&parseInfo)) { 1286 setResult(parseInfo, errorMessagePointer, errorDescription); 1287 kdbWarning() << "message=" << *errorMessagePointer 1288 << "description=" << *errorDescriptionPointer; 1289 kdbWarning() << newWhereExpr; 1290 d->whereExpr = KDbExpression(); 1291 return false; 1292 } 1293 errorMessagePointer->clear(); 1294 errorDescriptionPointer->clear(); 1295 KDbQuerySchemaPrivate::setWhereExpressionInternal(this, newWhereExpr); 1296 return true; 1297 } 1298 1299 bool KDbQuerySchema::addToWhereExpression(KDbField *field, const QVariant &value, 1300 KDbToken relation, QString *errorMessage, 1301 QString *errorDescription) 1302 { 1303 KDbToken token; 1304 if (value.isNull()) { 1305 token = KDbToken::SQL_NULL; 1306 } else { 1307 const KDbField::Type type = field->type(); // cache: evaluating type of expressions can be expensive 1308 if (KDbField::isIntegerType(type)) { 1309 token = KDbToken::INTEGER_CONST; 1310 } else if (KDbField::isFPNumericType(type)) { 1311 token = KDbToken::REAL_CONST; 1312 } else { 1313 token = KDbToken::CHARACTER_STRING_LITERAL; 1314 } 1315 //! @todo date, time 1316 } 1317 1318 KDbBinaryExpression newExpr( 1319 KDbConstExpression(token, value), 1320 relation, 1321 KDbVariableExpression((field->table() ? (field->table()->name() + QLatin1Char('.')) : QString()) + field->name()) 1322 ); 1323 const KDbExpression origWhereExpr = d->whereExpr; 1324 if (!d->whereExpr.isNull()) { 1325 newExpr = KDbBinaryExpression( 1326 d->whereExpr, 1327 KDbToken::AND, 1328 newExpr 1329 ); 1330 } 1331 const bool result = setWhereExpression(newExpr, errorMessage, errorDescription); 1332 if (!result) { // revert, setWhereExpression() cleared it 1333 d->whereExpr = origWhereExpr; 1334 } 1335 return result; 1336 } 1337 1338 /* 1339 void KDbQuerySchema::addToWhereExpression(KDbField *field, const QVariant& value) 1340 switch (value.type()) { 1341 case Int: case UInt: case Bool: case LongLong: case ULongLong: 1342 token = INTEGER_CONST; 1343 break; 1344 case Double: 1345 token = REAL_CONST; 1346 break; 1347 default: 1348 token = CHARACTER_STRING_LITERAL; 1349 } 1350 //! @todo date, time 1351 1352 */ 1353 1354 KDbExpression KDbQuerySchema::whereExpression() const 1355 { 1356 return d->whereExpr; 1357 } 1358 1359 void KDbQuerySchema::setOrderByColumnList(const KDbOrderByColumnList& list) 1360 { 1361 delete d->orderByColumnList; 1362 d->orderByColumnList = new KDbOrderByColumnList(list, nullptr, nullptr, nullptr); 1363 // all field names should be found, exit otherwise ..........? 1364 } 1365 1366 KDbOrderByColumnList* KDbQuerySchema::orderByColumnList() 1367 { 1368 return d->orderByColumnList; 1369 } 1370 1371 const KDbOrderByColumnList* KDbQuerySchema::orderByColumnList() const 1372 { 1373 return d->orderByColumnList; 1374 } 1375 1376 QList<KDbQuerySchemaParameter> KDbQuerySchema::parameters(KDbConnection *conn) const 1377 { 1378 QList<KDbQuerySchemaParameter> params; 1379 const KDbQueryColumnInfo::Vector fieldsExpanded(this->fieldsExpanded(conn)); 1380 for (int i = 0; i < fieldsExpanded.count(); ++i) { 1381 KDbQueryColumnInfo *ci = fieldsExpanded[i]; 1382 if (!ci->field()->expression().isNull()) { 1383 ci->field()->expression().getQueryParameters(¶ms); 1384 } 1385 } 1386 KDbExpression where = whereExpression(); 1387 if (!where.isNull()) { 1388 where.getQueryParameters(¶ms); 1389 } 1390 return params; 1391 } 1392 1393 bool KDbQuerySchema::validate(QString *errorMessage, QString *errorDescription) 1394 { 1395 KDbParseInfoInternal parseInfo(this); 1396 foreach(KDbField* f, *fields()) { 1397 if (f->isExpression()) { 1398 if (!f->expression().validate(&parseInfo)) { 1399 setResult(parseInfo, errorMessage, errorDescription); 1400 return false; 1401 } 1402 } 1403 } 1404 if (!whereExpression().validate(&parseInfo)) { 1405 setResult(parseInfo, errorMessage, errorDescription); 1406 return false; 1407 } 1408 return true; 1409 }