KDb

KDbRelationship.cpp
1 /* This file is part of the KDE project
2  Copyright (C) 2003-2017 JarosÅ‚aw Staniek <[email protected]>
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 
27 class Q_DECL_HIDDEN KDbRelationship::Private
28 {
29 public:
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 
181 private:
182  KDbRelationship * const q;
183 };
184 
185 KDbRelationship::KDbRelationship()
186  : d(new Private(this))
187 {
188 }
189 
190 KDbRelationship::KDbRelationship(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex)
191  : KDbRelationship()
192 {
193  (void)setIndices(masterIndex, detailsIndex);
194 }
195 
196 KDbRelationship::KDbRelationship(KDbQuerySchema *query, KDbField *field1, KDbField *field2)
197  : KDbRelationship()
198 {
199  d->createIndices(query, field1, field2);
200 }
201 
202 KDbRelationship::~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 
213 KDbRelationship& KDbRelationship::operator=(KDbRelationship &other)
214 {
215  (void)setIndices(other.masterIndex(), other.detailsIndex());
216  return *this;
217 }
218 
219 bool KDbRelationship::operator==(const KDbRelationship& other) const
220 {
221  return d->masterIndex == other.masterIndex() && d->detailsIndex == other.detailsIndex();
222 }
223 
224 KDbIndexSchema *KDbRelationship::masterIndex()
225 {
226  return d->masterIndex;
227 }
228 
229 const KDbIndexSchema *KDbRelationship::masterIndex() const
230 {
231  return d->masterIndex;
232 }
233 
234 KDbIndexSchema *KDbRelationship::detailsIndex()
235 {
236  return d->detailsIndex;
237 }
238 
239 const KDbIndexSchema *KDbRelationship::detailsIndex() const
240 {
241  return d->detailsIndex;
242 }
243 
244 KDbField::PairList *KDbRelationship::fieldPairs()
245 {
246  return &d->pairs;
247 }
248 
249 const KDbField::PairList *KDbRelationship::fieldPairs() const
250 {
251  return &d->pairs;
252 }
253 
254 bool KDbRelationship::isEmpty() const
255 {
256  return d->pairs.isEmpty();
257 }
258 
259 KDbTableSchema* KDbRelationship::masterTable()
260 {
261  return d->masterIndex ? d->masterIndex->table() : nullptr;
262 }
263 
264 const KDbTableSchema* KDbRelationship::masterTable() const
265 {
266  return d->masterIndex ? d->masterIndex->table() : nullptr;
267 }
268 
269 KDbTableSchema* KDbRelationship::detailsTable()
270 {
271  return d->detailsIndex ? d->detailsIndex->table() : nullptr;
272 }
273 
274 const KDbTableSchema* KDbRelationship::detailsTable() const
275 {
276  return d->detailsIndex ? d->detailsIndex->table() : nullptr;
277 }
278 
279 bool KDbRelationship::setIndices(KDbIndexSchema* masterIndex, KDbIndexSchema* detailsIndex)
280 {
281  return d->setIndices(masterIndex, detailsIndex, true);
282 }
bool isPrimaryKey() const
Definition: KDbField.h:287
KDbIndexSchema * primaryKey()
bool isQueryAsterisk() const
Definition: KDbField.h:640
KDbTableSchema * table()
Definition: KDbField.cpp:585
void clear()
QString name
QList::const_iterator constBegin() const const
bool contains(const T &value) const const
static QString defaultSqlTypeName(KDbField::Type type)
Definition: KDbDriver.cpp:164
bool isUnsigned() const
if the type has the unsigned attribute
Definition: KDbField.h:515
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
bool isTextType() const
Definition: KDbField.h:353
bool isIntegerType() const
Definition: KDbField.h:326
bool isEmpty() const const
bool addIndex(KDbIndexSchema *index)
Adds index index to this table schema Ownership of the index is transferred to the table schema.
QList< KDbField * >::ConstIterator ListIterator
iterator for list of fields
Definition: KDbField.h:79
Type type() const
Definition: KDbField.cpp:379
KDbField::List * fields()
Provides information about database index that can be created for a database table.
QList::const_iterator constEnd() const const
KDbQuerySchema provides information about database query.
Meta-data for a field.
Definition: KDbField.h:71
int fieldCount() const
QString name() const
Definition: KDbField.cpp:256
KDbTableSchema * table()
void attachRelationship(KDbRelationship *rel)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Jun 25 2022 06:21:33 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.