File indexing completed on 2024-09-08 10:13:03
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org> 0003 0004 This program 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 program 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 program; see the file COPYING. 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 "KDbRelationship.h" 0021 #include "KDbIndexSchema.h" 0022 #include "KDbTableSchema.h" 0023 #include "KDbQuerySchema.h" 0024 #include "KDbDriver.h" 0025 #include "kdb_debug.h" 0026 0027 class Q_DECL_HIDDEN KDbRelationship::Private 0028 { 0029 public: 0030 Private(KDbRelationship *r) 0031 : q(r) 0032 { 0033 } 0034 0035 void createIndices(KDbQuerySchema *query, KDbField *field1, KDbField *field2) 0036 { 0037 if (!field1 || !field2 || !query) { 0038 kdbWarning() << "!masterField || !detailsField || !query"; 0039 return; 0040 } 0041 if (field1->isQueryAsterisk() || field2->isQueryAsterisk()) { 0042 kdbWarning() << "relationship's fields cannot be asterisks"; 0043 return; 0044 } 0045 if (field1->table() == field2->table()) { 0046 kdbWarning() << "fields cannot belong to the same table"; 0047 return; 0048 } 0049 if (!query->contains(field1->table()) || !query->contains(field2->table())) { 0050 kdbWarning() << "fields do not belong to this query"; 0051 return; 0052 } 0053 //! @todo: check more things: -types 0054 //! @todo: find existing global db relationships 0055 0056 KDbField *masterField = nullptr; 0057 KDbField *detailsField = nullptr; 0058 bool p1 = field1->isPrimaryKey(), p2 = field2->isPrimaryKey(); 0059 if (p1 && p2) { 0060 //2 primary keys 0061 masterField = field1; 0062 masterIndex = masterField->table()->primaryKey(); 0063 detailsField = field2; 0064 detailsIndex = detailsField->table()->primaryKey(); 0065 } else if (!p1 && p2) { 0066 //foreign + primary: swap 0067 KDbField *tmp = field1; 0068 field1 = field2; 0069 field2 = tmp; 0070 p1 = !p1; 0071 p2 = !p2; 0072 } 0073 0074 if (p1 && !p2) { 0075 //primary + foreign 0076 masterField = field1; 0077 masterIndex = masterField->table()->primaryKey(); 0078 detailsField = field2; 0079 //create foreign key 0080 //! @todo: check if it already exists 0081 detailsIndex = new KDbIndexSchema; 0082 detailsField->table()->addIndex(detailsIndex); 0083 detailsIndexOwned = true; 0084 const bool ok = detailsIndex->addField(detailsField); 0085 Q_ASSERT(ok); 0086 detailsIndex->setForeignKey(true); 0087 } else if (!p1 && !p2) { 0088 masterField = field1; 0089 masterIndex = new KDbIndexSchema; 0090 masterField->table()->addIndex(masterIndex); 0091 masterIndexOwned = true; 0092 bool ok = masterIndex->addField(masterField); 0093 Q_ASSERT(ok); 0094 masterIndex->setForeignKey(true); 0095 0096 detailsField = field2; 0097 detailsIndex = new KDbIndexSchema; 0098 detailsField->table()->addIndex(detailsIndex); 0099 detailsIndexOwned = true; 0100 ok = detailsIndex->addField(detailsField); 0101 Q_ASSERT(ok); 0102 detailsIndex->setForeignKey(true); 0103 } 0104 0105 if (!masterIndex || !detailsIndex) { 0106 return; //failed 0107 } 0108 0109 (void)setIndices(masterIndex, detailsIndex, false); 0110 } 0111 0112 /*! Internal version of setIndices(). @a ownedByMaster parameter is passed 0113 to KDbIndexSchema::attachRelationship() */ 0114 bool setIndices(KDbIndexSchema *newMasterIndex, KDbIndexSchema *newDetailsIndex, 0115 bool ownedByMaster) 0116 { 0117 masterIndex = nullptr; 0118 detailsIndex = nullptr; 0119 pairs.clear(); 0120 if (!newMasterIndex || !newDetailsIndex || !newMasterIndex->table() 0121 || !newDetailsIndex->table() || newMasterIndex->table() == newDetailsIndex->table() 0122 || newMasterIndex->fieldCount() != newDetailsIndex->fieldCount()) { 0123 return false; 0124 } 0125 const KDbField::List *masterIndexFields = newMasterIndex->fields(); 0126 const KDbField::List *detailsIndexFields = newDetailsIndex->fields(); 0127 KDbField::ListIterator masterIt(masterIndexFields->constBegin()); 0128 KDbField::ListIterator detailsIt(detailsIndexFields->constBegin()); 0129 for (; masterIt != masterIndexFields->constEnd() 0130 && detailsIt != detailsIndexFields->constEnd(); 0131 ++masterIt, ++detailsIt) { 0132 KDbField *masterField = *masterIt; 0133 KDbField *detailsField = *detailsIt; 0134 const KDbField::Type masterType 0135 = masterField->type(); // cache: evaluating type of expressions can be expensive 0136 const KDbField::Type detailsType = detailsField->type(); 0137 if (masterType != detailsType 0138 && KDbField::isIntegerType(masterType) != KDbField::isIntegerType(detailsType) 0139 && KDbField::isTextType(masterType) != KDbField::isTextType(detailsType)) { 0140 kdbWarning() << "INDEX on" << newMasterIndex->table()->name() << ", INDEX on" 0141 << newDetailsIndex->table()->name() 0142 << ": !equal field types:" << KDbDriver::defaultSqlTypeName(masterType) 0143 << masterField->name() << "," 0144 << KDbDriver::defaultSqlTypeName(detailsType) << detailsField->name(); 0145 pairs.clear(); 0146 return false; 0147 } 0148 #if 0 // too STRICT! 0149 if ((masterField->isUnsigned() && !detailsField->isUnsigned()) 0150 || (!masterField->isUnsigned() && detailsField->isUnsigned())) { 0151 kdbWarning() << "KDbRelationship::setIndices(INDEX on '" << masterIndex->table()->name() 0152 << "',INDEX on " << detailsIndex->table()->name() << "): !equal signedness of field types: " 0153 << KDbDriver::defaultSqlTypeName(masterField->type()) << " " << masterField->name() << ", " 0154 << KDbDriver::defaultSqlTypeName(detailsField->type()) << " " << detailsField->name(); 0155 pairs.clear(); 0156 return; 0157 } 0158 #endif 0159 pairs.append(KDbField::Pair(masterField, detailsField)); 0160 } 0161 // ok: update information 0162 if (masterIndex) { // detach yourself 0163 masterIndex->detachRelationship(q); 0164 } 0165 if (detailsIndex) { // detach yourself 0166 detailsIndex->detachRelationship(q); 0167 } 0168 masterIndex = newMasterIndex; 0169 detailsIndex = newDetailsIndex; 0170 masterIndex->attachRelationship(q, ownedByMaster); 0171 detailsIndex->attachRelationship(q, ownedByMaster); 0172 return true; 0173 } 0174 0175 KDbIndexSchema *masterIndex = nullptr; 0176 KDbIndexSchema *detailsIndex = nullptr; 0177 KDbField::PairList pairs; 0178 bool masterIndexOwned = false; 0179 bool detailsIndexOwned = false; 0180 0181 private: 0182 KDbRelationship * const q; 0183 }; 0184 0185 KDbRelationship::KDbRelationship() 0186 : d(new Private(this)) 0187 { 0188 } 0189 0190 KDbRelationship::KDbRelationship(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex) 0191 : KDbRelationship() 0192 { 0193 (void)setIndices(masterIndex, detailsIndex); 0194 } 0195 0196 KDbRelationship::KDbRelationship(KDbQuerySchema *query, KDbField *field1, KDbField *field2) 0197 : KDbRelationship() 0198 { 0199 d->createIndices(query, field1, field2); 0200 } 0201 0202 KDbRelationship::~KDbRelationship() 0203 { 0204 if (d->masterIndexOwned) { 0205 delete d->masterIndex; 0206 } 0207 if (d->detailsIndexOwned) { 0208 delete d->detailsIndex; 0209 } 0210 delete d; 0211 } 0212 0213 KDbRelationship& KDbRelationship::operator=(KDbRelationship &other) 0214 { 0215 (void)setIndices(other.masterIndex(), other.detailsIndex()); 0216 return *this; 0217 } 0218 0219 bool KDbRelationship::operator==(const KDbRelationship& other) const 0220 { 0221 return d->masterIndex == other.masterIndex() && d->detailsIndex == other.detailsIndex(); 0222 } 0223 0224 KDbIndexSchema *KDbRelationship::masterIndex() 0225 { 0226 return d->masterIndex; 0227 } 0228 0229 const KDbIndexSchema *KDbRelationship::masterIndex() const 0230 { 0231 return d->masterIndex; 0232 } 0233 0234 KDbIndexSchema *KDbRelationship::detailsIndex() 0235 { 0236 return d->detailsIndex; 0237 } 0238 0239 const KDbIndexSchema *KDbRelationship::detailsIndex() const 0240 { 0241 return d->detailsIndex; 0242 } 0243 0244 KDbField::PairList *KDbRelationship::fieldPairs() 0245 { 0246 return &d->pairs; 0247 } 0248 0249 const KDbField::PairList *KDbRelationship::fieldPairs() const 0250 { 0251 return &d->pairs; 0252 } 0253 0254 bool KDbRelationship::isEmpty() const 0255 { 0256 return d->pairs.isEmpty(); 0257 } 0258 0259 KDbTableSchema* KDbRelationship::masterTable() 0260 { 0261 return d->masterIndex ? d->masterIndex->table() : nullptr; 0262 } 0263 0264 const KDbTableSchema* KDbRelationship::masterTable() const 0265 { 0266 return d->masterIndex ? d->masterIndex->table() : nullptr; 0267 } 0268 0269 KDbTableSchema* KDbRelationship::detailsTable() 0270 { 0271 return d->detailsIndex ? d->detailsIndex->table() : nullptr; 0272 } 0273 0274 const KDbTableSchema* KDbRelationship::detailsTable() const 0275 { 0276 return d->detailsIndex ? d->detailsIndex->table() : nullptr; 0277 } 0278 0279 bool KDbRelationship::setIndices(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex) 0280 { 0281 return d->setIndices(masterIndex, detailsIndex, true); 0282 }