File indexing completed on 2024-12-01 04:19:14

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003 Joseph Wenninger <jowenn@kde.org>
0003    Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KDbTableSchema.h"
0022 #include "KDbDriver.h"
0023 #include "KDbConnection.h"
0024 #include "KDbLookupFieldSchema.h"
0025 #include "KDbQuerySchema.h"
0026 #include "kdb_debug.h"
0027 
0028 //! @internal
0029 class Q_DECL_HIDDEN KDbTableSchema::Private
0030 {
0031 public:
0032     Private(KDbTableSchema *t)
0033             : q(t)
0034             , anyNonPKField(nullptr)
0035             , conn(nullptr)
0036             , pkey(nullptr)
0037             , query(nullptr)
0038     {
0039     }
0040 
0041     ~Private() {
0042         clearLookupFields();
0043         qDeleteAll(indices);
0044         delete query;
0045     }
0046 
0047     void clearLookupFields() {
0048         qDeleteAll(lookupFields);
0049         lookupFields.clear();
0050     }
0051 
0052     void addIndex(KDbIndexSchema *index) {
0053         indices.append(index);
0054         index->setTable(q);
0055     }
0056 
0057     KDbTableSchema * const q;
0058     KDbField *anyNonPKField;
0059     QHash<const KDbField*, KDbLookupFieldSchema*> lookupFields;
0060     QVector<KDbLookupFieldSchema*> lookupFieldsList;
0061     QList<KDbIndexSchema*> indices;
0062     //! @todo IMPORTANT: use something like QPointer<KDbConnection> conn
0063     KDbConnection *conn;
0064     KDbIndexSchema *pkey;
0065     KDbQuerySchema *query; //!< cached query schema that is defined by "select * from <this_table_name>"
0066 private:
0067     Q_DISABLE_COPY(Private)
0068 };
0069 
0070 //-------------------------------------
0071 
0072 KDbTableSchema::KDbTableSchema(const QString& name)
0073         : KDbFieldList(true)
0074         , KDbObject(KDb::TableObjectType)
0075         , d(new Private(this))
0076 {
0077     setName(name);
0078     init(nullptr);
0079 }
0080 
0081 KDbTableSchema::KDbTableSchema(const KDbObject& other)
0082         : KDbFieldList(true)
0083         , KDbObject(other)
0084         , d(new Private(this))
0085 {
0086     init(nullptr);
0087 }
0088 
0089 KDbTableSchema::KDbTableSchema()
0090         : KDbFieldList(true)
0091         , KDbObject(KDb::TableObjectType)
0092         , d(new Private(this))
0093 {
0094     init(nullptr);
0095 }
0096 
0097 KDbTableSchema::KDbTableSchema(const KDbTableSchema& ts, bool copyId)
0098         : KDbFieldList(static_cast<const KDbFieldList&>(ts))
0099         , KDbObject(static_cast<const KDbObject&>(ts))
0100         , d(new Private(this))
0101 {
0102     init(ts, copyId);
0103 }
0104 
0105 KDbTableSchema::KDbTableSchema(const KDbTableSchema& ts, int id)
0106         : KDbFieldList(static_cast<const KDbFieldList&>(ts))
0107         , KDbObject(static_cast<const KDbObject&>(ts))
0108         , d(new Private(this))
0109 {
0110     init(ts, false);
0111     setId(id);
0112 }
0113 
0114 // used by KDbConnection
0115 KDbTableSchema::KDbTableSchema(KDbConnection *conn, const QString & name)
0116         : KDbFieldList(true)
0117         , KDbObject(KDb::TableObjectType)
0118         , d(new Private(this))
0119 {
0120     setName(name);
0121     init(conn);
0122 }
0123 
0124 KDbTableSchema::~KDbTableSchema()
0125 {
0126     if (d->conn) {
0127         d->conn->removeMe(this);
0128     }
0129     delete d;
0130 }
0131 
0132 void KDbTableSchema::init(KDbConnection* conn)
0133 {
0134     d->conn = conn;
0135     d->pkey = new KDbIndexSchema;
0136     d->addIndex(d->pkey);
0137 }
0138 
0139 void KDbTableSchema::init(const KDbTableSchema& ts, bool copyId)
0140 {
0141     d->conn = ts.connection();
0142     setName(ts.name());
0143     d->pkey = nullptr; //will be copied
0144     if (!copyId)
0145         setId(-1);
0146 
0147     //deep copy all members
0148     foreach(KDbIndexSchema* otherIdx, *ts.indices()) {
0149         // fields from _this_ table will be assigned to the index
0150         KDbIndexSchema *idx = copyIndexFrom(*otherIdx);
0151         if (idx->isPrimaryKey()) {//assign pkey
0152             d->pkey = idx;
0153         }
0154     }
0155 
0156     KDbField::ListIterator tsIter(ts.fieldsIterator());
0157     KDbField::ListIterator iter(fieldsIterator());
0158     for (; iter != fieldsIteratorConstEnd(); ++tsIter, ++iter) {
0159         const KDbLookupFieldSchema *lookup = ts.lookupFieldSchema(**tsIter);
0160         if (lookup) {
0161             d->lookupFields.insert(*iter, new KDbLookupFieldSchema(*lookup));
0162         }
0163     }
0164 }
0165 
0166 KDbIndexSchema* KDbTableSchema::primaryKey()
0167 {
0168     return d->pkey;
0169 }
0170 
0171 const KDbIndexSchema* KDbTableSchema::primaryKey() const
0172 {
0173     return d->pkey;
0174 }
0175 
0176 const QList<KDbIndexSchema*>::ConstIterator KDbTableSchema::indicesIterator() const
0177 {
0178     return QList<KDbIndexSchema*>::ConstIterator (d->indices.constBegin());
0179 }
0180 
0181 const QList<KDbIndexSchema*>* KDbTableSchema::indices() const
0182 {
0183     return &d->indices;
0184 }
0185 
0186 bool KDbTableSchema::addIndex(KDbIndexSchema *index)
0187 {
0188     if (index && !d->indices.contains(index)) {
0189         d->addIndex(index);
0190         return true;
0191     }
0192     return false;
0193 }
0194 
0195 bool KDbTableSchema::removeIndex(KDbIndexSchema *index)
0196 {
0197     if (index) {
0198         d->indices.removeOne(index);
0199         return true;
0200     }
0201     return false;
0202 }
0203 
0204 KDbIndexSchema* KDbTableSchema::copyIndexFrom(const KDbIndexSchema& index)
0205 {
0206     KDbIndexSchema *newIndex = new KDbIndexSchema(index, this);
0207     addIndex(newIndex);
0208     return newIndex;
0209 }
0210 
0211 bool KDbTableSchema::isInternal() const
0212 {
0213     return dynamic_cast<const KDbInternalTableSchema*>(this);
0214 }
0215 
0216 void KDbTableSchema::setPrimaryKey(KDbIndexSchema *pkey)
0217 {
0218     if (pkey && !d->indices.contains(pkey)) {
0219         kdbWarning() << *pkey << "index can't be made primary key because it does not belong "
0220                                  "to table schema" << name();
0221         return;
0222     }
0223     if (d->pkey && d->pkey != pkey) {
0224         if (d->pkey->fieldCount() == 0) {//this is empty key, probably default - remove it
0225             d->indices.removeOne(d->pkey);
0226             delete d->pkey;
0227         }
0228         else {
0229             d->pkey->setPrimaryKey(false); //there can be only one pkey..
0230             //that's ok, the old pkey is still on indices list, if not empty
0231         }
0232     }
0233 
0234     if (!pkey) {//clearing - set empty pkey
0235         pkey = new KDbIndexSchema;
0236         d->addIndex(pkey);
0237     }
0238     d->pkey = pkey; //!< @todo
0239     d->pkey->setPrimaryKey(true);
0240     d->anyNonPKField = nullptr; //for safety
0241 }
0242 
0243 bool KDbTableSchema::insertField(int index, KDbField *field)
0244 {
0245     if (!field) {
0246         return false;
0247     }
0248     KDbField::List *fieldsList = fields();
0249     KDbFieldList::insertField(index, field);
0250     if (!field || index > fieldsList->count()) {
0251         return false;
0252     }
0253     field->setTable(this);
0254     field->setOrder(index);
0255     //update order for next next fields
0256     const int fieldCount = fieldsList->count();
0257     for (int i = index + 1; i < fieldCount; i++) {
0258         fieldsList->at(i)->setOrder(i);
0259     }
0260 
0261     //Check for auto-generated indices:
0262     KDbIndexSchema *idx = nullptr;
0263     if (field->isPrimaryKey()) {// this is auto-generated single-field unique index
0264         idx = new KDbIndexSchema;
0265         d->addIndex(idx);
0266         idx->setAutoGenerated(true);
0267         const bool ok = idx->addField(field);
0268         Q_ASSERT(ok);
0269         setPrimaryKey(idx);
0270     }
0271     if (field->isUniqueKey()) {
0272         if (!idx) {
0273             idx = new KDbIndexSchema;
0274             d->addIndex(idx);
0275             idx->setAutoGenerated(true);
0276             const bool ok = idx->addField(field);
0277             Q_ASSERT(ok);
0278         }
0279         idx->setUnique(true);
0280     }
0281     if (field->isIndexed()) {// this is auto-generated single-field
0282         if (!idx) {
0283             idx = new KDbIndexSchema;
0284             d->addIndex(idx);
0285             idx->setAutoGenerated(true);
0286             const bool ok = idx->addField(field);
0287             Q_ASSERT(ok);
0288         }
0289     }
0290     return true;
0291 }
0292 
0293 bool KDbTableSchema::removeField(KDbField *field)
0294 {
0295     KDbLookupFieldSchema* lookup = d->lookupFields.take(field);
0296     if (!KDbFieldList::removeField(field)) {
0297         return false;
0298     }
0299     if (d->anyNonPKField && field == d->anyNonPKField) //d->anyNonPKField will be removed!
0300         d->anyNonPKField = nullptr;
0301     delete lookup;
0302     return true;
0303 }
0304 
0305 void KDbTableSchema::clear()
0306 {
0307     d->indices.clear();
0308     d->clearLookupFields();
0309     KDbFieldList::clear();
0310     KDbObject::clear();
0311     d->conn = nullptr;
0312 }
0313 
0314 QDebug KDbTableSchema::debugFields(QDebug dbg) const
0315 {
0316     dbg.nospace() << static_cast<const KDbFieldList&>(*this);
0317     for (const KDbField *f : *fields()) {
0318         const KDbLookupFieldSchema *lookupSchema = lookupFieldSchema(*f);
0319         if (lookupSchema)
0320             dbg.nospace() << '\n' << f->name() << *lookupSchema;
0321     }
0322     return dbg.space();
0323 }
0324 
0325 QDebug operator<<(QDebug dbg, const KDbTableSchema& table)
0326 {
0327     dbg.nospace() << "TABLE";
0328     dbg.space() << static_cast<const KDbObject&>(table) << '\n';
0329     table.debugFields(dbg);
0330     return dbg.space();
0331 }
0332 
0333 QDebug operator<<(QDebug dbg, const KDbInternalTableSchema& table)
0334 {
0335     dbg.nospace() << "INTERNAL_TABLE";
0336     dbg.space() << static_cast<const KDbObject&>(table) << '\n';
0337     table.debugFields(dbg);
0338     return dbg.space();
0339 }
0340 
0341 KDbConnection* KDbTableSchema::connection() const
0342 {
0343     return d->conn;
0344 }
0345 
0346 void KDbTableSchema::setConnection(KDbConnection* conn)
0347 {
0348     d->conn = conn;
0349 }
0350 
0351 KDbQuerySchema* KDbTableSchema::query()
0352 {
0353     if (d->query)
0354         return d->query;
0355     d->query = new KDbQuerySchema(this);   //it's owned by me
0356     return d->query;
0357 }
0358 
0359 KDbField* KDbTableSchema::anyNonPKField()
0360 {
0361     if (!d->anyNonPKField) {
0362         KDbField *f = nullptr;
0363         for (QListIterator<KDbField*> it(*fields()); it.hasPrevious();) {
0364             f = it.previous();
0365             if (!f->isPrimaryKey() && (!d->pkey || !d->pkey->hasField(*f)))
0366                 break;
0367         }
0368         d->anyNonPKField = f;
0369     }
0370     return d->anyNonPKField;
0371 }
0372 
0373 bool KDbTableSchema::setLookupFieldSchema(const QString& fieldName, KDbLookupFieldSchema *lookupFieldSchema)
0374 {
0375     KDbField *f = field(fieldName);
0376     if (!f) {
0377         kdbWarning() << "no such field" << fieldName << "in table" << name();
0378         return false;
0379     }
0380     delete d->lookupFields.take(f);
0381     if (lookupFieldSchema) {
0382         d->lookupFields.insert(f, lookupFieldSchema);
0383     }
0384     d->lookupFieldsList.clear(); //this will force to rebuid the internal cache
0385     return true;
0386 }
0387 
0388 KDbLookupFieldSchema *KDbTableSchema::lookupFieldSchema(const KDbField& field)
0389 {
0390     return d->lookupFields.value(&field);
0391 }
0392 
0393 const KDbLookupFieldSchema *KDbTableSchema::lookupFieldSchema(const KDbField& field) const
0394 {
0395     return d->lookupFields.value(&field);
0396 }
0397 
0398 KDbLookupFieldSchema *KDbTableSchema::lookupFieldSchema(const QString& fieldName)
0399 {
0400     KDbField *f = KDbTableSchema::field(fieldName);
0401     if (!f)
0402         return nullptr;
0403     return lookupFieldSchema(*f);
0404 }
0405 
0406 QVector<KDbLookupFieldSchema*> KDbTableSchema::lookupFields() const
0407 {
0408     if (d->lookupFields.isEmpty())
0409         return QVector<KDbLookupFieldSchema*>();
0410     if (!d->lookupFields.isEmpty() && !d->lookupFieldsList.isEmpty())
0411         return d->lookupFieldsList; //already updated
0412     //update
0413     d->lookupFieldsList.clear();
0414     d->lookupFieldsList.resize(d->lookupFields.count());
0415     int i = 0;
0416     for (KDbField* f : *fields()) {
0417         QHash<const KDbField*, KDbLookupFieldSchema*>::ConstIterator itMap = d->lookupFields.constFind(f);
0418         if (itMap != d->lookupFields.constEnd()) {
0419             d->lookupFieldsList[i] = itMap.value();
0420             i++;
0421         }
0422     }
0423     return d->lookupFieldsList;
0424 }
0425 
0426 //--------------------------------------
0427 
0428 class Q_DECL_HIDDEN KDbInternalTableSchema::Private
0429 {
0430 public:
0431     Private() {}
0432     bool dummy = false;
0433 };
0434 
0435 KDbInternalTableSchema::KDbInternalTableSchema(const QString& name)
0436         : KDbTableSchema(name)
0437         , d(new Private)
0438 {
0439 }
0440 
0441 KDbInternalTableSchema::KDbInternalTableSchema(const KDbTableSchema& ts)
0442         : KDbTableSchema(ts, false)
0443         , d(new Private)
0444 {
0445 }
0446 
0447 KDbInternalTableSchema::KDbInternalTableSchema(const KDbInternalTableSchema& ts)
0448         : KDbTableSchema(ts, false)
0449         , d(new Private)
0450 {
0451 }
0452 
0453 KDbInternalTableSchema::~KDbInternalTableSchema()
0454 {
0455     delete d;
0456 }