KDb

KDbRelationship.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include "KDbRelationship.h"
21#include "KDbIndexSchema.h"
22#include "KDbTableSchema.h"
23#include "KDbQuerySchema.h"
24#include "KDbDriver.h"
25#include "kdb_debug.h"
26
27class Q_DECL_HIDDEN KDbRelationship::Private
28{
29public:
30 Private(KDbRelationship *r)
31 : q(r)
32 {
33 }
34
35 void createIndices(KDbQuerySchema *query, KDbField *field1, KDbField *field2)
36 {
37 if (!field1 || !field2 || !query) {
38 kdbWarning() << "!masterField || !detailsField || !query";
39 return;
40 }
41 if (field1->isQueryAsterisk() || field2->isQueryAsterisk()) {
42 kdbWarning() << "relationship's fields cannot be asterisks";
43 return;
44 }
45 if (field1->table() == field2->table()) {
46 kdbWarning() << "fields cannot belong to the same table";
47 return;
48 }
49 if (!query->contains(field1->table()) || !query->contains(field2->table())) {
50 kdbWarning() << "fields do not belong to this query";
51 return;
52 }
53 //! @todo: check more things: -types
54 //! @todo: find existing global db relationships
55
56 KDbField *masterField = nullptr;
57 KDbField *detailsField = nullptr;
58 bool p1 = field1->isPrimaryKey(), p2 = field2->isPrimaryKey();
59 if (p1 && p2) {
60 //2 primary keys
61 masterField = field1;
62 masterIndex = masterField->table()->primaryKey();
63 detailsField = field2;
64 detailsIndex = detailsField->table()->primaryKey();
65 } else if (!p1 && p2) {
66 //foreign + primary: swap
67 KDbField *tmp = field1;
68 field1 = field2;
69 field2 = tmp;
70 p1 = !p1;
71 p2 = !p2;
72 }
73
74 if (p1 && !p2) {
75 //primary + foreign
76 masterField = field1;
77 masterIndex = masterField->table()->primaryKey();
78 detailsField = field2;
79 //create foreign key
80 //! @todo: check if it already exists
81 detailsIndex = new KDbIndexSchema;
82 detailsField->table()->addIndex(detailsIndex);
83 detailsIndexOwned = true;
84 const bool ok = detailsIndex->addField(detailsField);
85 Q_ASSERT(ok);
86 detailsIndex->setForeignKey(true);
87 } else if (!p1 && !p2) {
88 masterField = field1;
89 masterIndex = new KDbIndexSchema;
90 masterField->table()->addIndex(masterIndex);
91 masterIndexOwned = true;
92 bool ok = masterIndex->addField(masterField);
93 Q_ASSERT(ok);
94 masterIndex->setForeignKey(true);
95
96 detailsField = field2;
97 detailsIndex = new KDbIndexSchema;
98 detailsField->table()->addIndex(detailsIndex);
99 detailsIndexOwned = true;
100 ok = detailsIndex->addField(detailsField);
101 Q_ASSERT(ok);
102 detailsIndex->setForeignKey(true);
103 }
104
105 if (!masterIndex || !detailsIndex) {
106 return; //failed
107 }
108
109 (void)setIndices(masterIndex, detailsIndex, false);
110 }
111
112 /*! Internal version of setIndices(). @a ownedByMaster parameter is passed
113 to KDbIndexSchema::attachRelationship() */
114 bool setIndices(KDbIndexSchema *newMasterIndex, KDbIndexSchema *newDetailsIndex,
115 bool ownedByMaster)
116 {
117 masterIndex = nullptr;
118 detailsIndex = nullptr;
119 pairs.clear();
120 if (!newMasterIndex || !newDetailsIndex || !newMasterIndex->table()
121 || !newDetailsIndex->table() || newMasterIndex->table() == newDetailsIndex->table()
122 || newMasterIndex->fieldCount() != newDetailsIndex->fieldCount()) {
123 return false;
124 }
125 const KDbField::List *masterIndexFields = newMasterIndex->fields();
126 const KDbField::List *detailsIndexFields = newDetailsIndex->fields();
127 KDbField::ListIterator masterIt(masterIndexFields->constBegin());
128 KDbField::ListIterator detailsIt(detailsIndexFields->constBegin());
129 for (; masterIt != masterIndexFields->constEnd()
130 && detailsIt != detailsIndexFields->constEnd();
131 ++masterIt, ++detailsIt) {
132 KDbField *masterField = *masterIt;
133 KDbField *detailsField = *detailsIt;
134 const KDbField::Type masterType
135 = masterField->type(); // cache: evaluating type of expressions can be expensive
136 const KDbField::Type detailsType = detailsField->type();
137 if (masterType != detailsType
138 && KDbField::isIntegerType(masterType) != KDbField::isIntegerType(detailsType)
139 && KDbField::isTextType(masterType) != KDbField::isTextType(detailsType)) {
140 kdbWarning() << "INDEX on" << newMasterIndex->table()->name() << ", INDEX on"
141 << newDetailsIndex->table()->name()
142 << ": !equal field types:" << KDbDriver::defaultSqlTypeName(masterType)
143 << masterField->name() << ","
144 << KDbDriver::defaultSqlTypeName(detailsType) << detailsField->name();
145 pairs.clear();
146 return false;
147 }
148#if 0 // too STRICT!
149 if ((masterField->isUnsigned() && !detailsField->isUnsigned())
150 || (!masterField->isUnsigned() && detailsField->isUnsigned())) {
151 kdbWarning() << "KDbRelationship::setIndices(INDEX on '" << masterIndex->table()->name()
152 << "',INDEX on " << detailsIndex->table()->name() << "): !equal signedness of field types: "
153 << KDbDriver::defaultSqlTypeName(masterField->type()) << " " << masterField->name() << ", "
154 << KDbDriver::defaultSqlTypeName(detailsField->type()) << " " << detailsField->name();
155 pairs.clear();
156 return;
157 }
158#endif
159 pairs.append(KDbField::Pair(masterField, detailsField));
160 }
161 // ok: update information
162 if (masterIndex) { // detach yourself
163 masterIndex->detachRelationship(q);
164 }
165 if (detailsIndex) { // detach yourself
166 detailsIndex->detachRelationship(q);
167 }
168 masterIndex = newMasterIndex;
169 detailsIndex = newDetailsIndex;
170 masterIndex->attachRelationship(q, ownedByMaster);
171 detailsIndex->attachRelationship(q, ownedByMaster);
172 return true;
173 }
174
175 KDbIndexSchema *masterIndex = nullptr;
176 KDbIndexSchema *detailsIndex = nullptr;
177 KDbField::PairList pairs;
178 bool masterIndexOwned = false;
179 bool detailsIndexOwned = false;
180
181private:
182 KDbRelationship * const q;
183};
184
185KDbRelationship::KDbRelationship()
186 : d(new Private(this))
187{
188}
189
190KDbRelationship::KDbRelationship(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex)
191 : KDbRelationship()
192{
193 (void)setIndices(masterIndex, detailsIndex);
194}
195
196KDbRelationship::KDbRelationship(KDbQuerySchema *query, KDbField *field1, KDbField *field2)
197 : KDbRelationship()
198{
199 d->createIndices(query, field1, field2);
200}
201
202KDbRelationship::~KDbRelationship()
203{
204 if (d->masterIndexOwned) {
205 delete d->masterIndex;
206 }
207 if (d->detailsIndexOwned) {
208 delete d->detailsIndex;
209 }
210 delete d;
211}
212
213KDbRelationship& KDbRelationship::operator=(KDbRelationship &other)
214{
215 (void)setIndices(other.masterIndex(), other.detailsIndex());
216 return *this;
217}
218
219bool KDbRelationship::operator==(const KDbRelationship& other) const
220{
221 return d->masterIndex == other.masterIndex() && d->detailsIndex == other.detailsIndex();
222}
223
224KDbIndexSchema *KDbRelationship::masterIndex()
225{
226 return d->masterIndex;
227}
228
229const KDbIndexSchema *KDbRelationship::masterIndex() const
230{
231 return d->masterIndex;
232}
233
234KDbIndexSchema *KDbRelationship::detailsIndex()
235{
236 return d->detailsIndex;
237}
238
239const KDbIndexSchema *KDbRelationship::detailsIndex() const
240{
241 return d->detailsIndex;
242}
243
244KDbField::PairList *KDbRelationship::fieldPairs()
245{
246 return &d->pairs;
247}
248
249const KDbField::PairList *KDbRelationship::fieldPairs() const
250{
251 return &d->pairs;
252}
253
254bool KDbRelationship::isEmpty() const
255{
256 return d->pairs.isEmpty();
257}
258
259KDbTableSchema* KDbRelationship::masterTable()
260{
261 return d->masterIndex ? d->masterIndex->table() : nullptr;
262}
263
264const KDbTableSchema* KDbRelationship::masterTable() const
265{
266 return d->masterIndex ? d->masterIndex->table() : nullptr;
267}
268
269KDbTableSchema* KDbRelationship::detailsTable()
270{
271 return d->detailsIndex ? d->detailsIndex->table() : nullptr;
272}
273
274const KDbTableSchema* KDbRelationship::detailsTable() const
275{
276 return d->detailsIndex ? d->detailsIndex->table() : nullptr;
277}
278
279bool KDbRelationship::setIndices(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex)
280{
281 return d->setIndices(masterIndex, detailsIndex, true);
282}
static QString defaultSqlTypeName(KDbField::Type type)
int fieldCount() const
KDbField::List * fields()
Meta-data for a field.
Definition KDbField.h:72
KDbTableSchema * table()
Definition KDbField.cpp:585
bool isTextType() const
Definition KDbField.h:353
QString name() const
Definition KDbField.cpp:256
bool isQueryAsterisk() const
Definition KDbField.h:640
QList< KDbField * >::ConstIterator ListIterator
iterator for list of fields
Definition KDbField.h:79
QPair< KDbField *, KDbField * > Pair
fields pair
Definition KDbField.h:80
Type type() const
Definition KDbField.cpp:379
bool isIntegerType() const
Definition KDbField.h:326
bool isUnsigned() const
if the type has the unsigned attribute
Definition KDbField.h:515
bool isPrimaryKey() const
Definition KDbField.h:287
Provides information about database index that can be created for a database table.
void attachRelationship(KDbRelationship *rel)
KDbTableSchema * table()
KDbQuerySchema provides information about database query.
KDbIndexSchema * primaryKey()
bool addIndex(KDbIndexSchema *index)
Adds index index to this table schema Ownership of the index is transferred to the table schema.
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(const AT &value) const const
bool isEmpty() const const
void clear()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:07 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.