File indexing completed on 2024-03-24 16:11:21

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 }