kexi

relationship.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2003-2004 Jaroslaw Staniek <js@iidea.pl>
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 <kexidb/relationship.h>
21
22#include <kexidb/indexschema.h>
23#include <kexidb/tableschema.h>
24#include <kexidb/queryschema.h>
25#include <kexidb/driver.h>
26
27#include <kdebug.h>
28
29using namespace KexiDB;
30
31Relationship::Relationship()
32 : m_masterIndex(0)
33 , m_detailsIndex(0)
34 , m_masterIndexOwned(false)
35 , m_detailsIndexOwned(false)
36{
37 m_pairs.setAutoDelete(true);
38}
39
40Relationship::Relationship(IndexSchema* masterIndex, IndexSchema* detailsIndex)
41 : m_masterIndex(0)
42 , m_detailsIndex(0)
43 , m_masterIndexOwned(false)
44 , m_detailsIndexOwned(false)
45{
46 m_pairs.setAutoDelete(true);
47 setIndices(masterIndex, detailsIndex);
48}
49
50Relationship::Relationship( QuerySchema *query, Field *field1, Field *field2 )
51 : m_masterIndex(0)
52 , m_detailsIndex(0)
53 , m_masterIndexOwned(false)
54 , m_detailsIndexOwned(false)
55{
56 m_pairs.setAutoDelete(true);
57 createIndices( query, field1, field2 );
58}
59
60Relationship::~Relationship()
61{
62 if (m_masterIndexOwned)
63 delete m_masterIndex;
64 if (m_detailsIndexOwned)
65 delete m_detailsIndex;
66}
67
68void Relationship::createIndices( QuerySchema *query, Field *field1, Field *field2 )
69{
70 if (!field1 || !field2 || !query) {
71 KexiDBWarn << "Relationship::addRelationship(): !masterField || !detailsField || !query" << endl;
72 return;
73 }
74 if (field1->isQueryAsterisk() || field2->isQueryAsterisk()) {
75 KexiDBWarn << "Relationship::addRelationship(): relationship's fields cannot be asterisks" << endl;
76 return;
77 }
78 if (field1->table() == field2->table()) {
79 KexiDBWarn << "Relationship::addRelationship(): fields cannot belong to the same table" << endl;
80 return;
81 }
82// if (!query->hasField(field1) && !query->hasField(field2)) {
83 if (!query->contains(field1->table()) || !query->contains(field2->table())) {
84 KexiDBWarn << "Relationship::addRelationship(): fields do not belong to this query" << endl;
85 return;
86 }
87//@todo: check more things: -types
88//@todo: find existing global db relationships
89
90 Field *masterField = 0, *detailsField = 0;
91 bool p1 = field1->isPrimaryKey(), p2 = field2->isPrimaryKey();
92 if (p1 && p2) {
93 //2 primary keys
94 masterField = field1;
95 m_masterIndex = masterField->table()->primaryKey();
96 detailsField = field2;
97 m_detailsIndex = detailsField->table()->primaryKey();
98 }
99 else if (!p1 && p2) {
100 //foreign + primary: swap
101 Field *tmp = field1;
102 field1 = field2;
103 field2 = tmp;
104 p1 = !p1;
105 p2 = !p2;
106 }
107
108 if (p1 && !p2) {
109 //primary + foreign
110 masterField = field1;
111 m_masterIndex = masterField->table()->primaryKey();
112 detailsField = field2;
113 //create foreign key
114//@todo: check if it already exists
115 m_detailsIndex = new IndexSchema(detailsField->table());
116 m_detailsIndexOwned = true;
117 m_detailsIndex->addField(detailsField);
118 m_detailsIndex->setForeignKey(true);
119 }
120 else if (!p1 && !p2) {
121 masterField = field1;
122 m_masterIndex = new IndexSchema(masterField->table());
123 m_masterIndexOwned = true;
124 m_masterIndex->addField(masterField);
125 m_masterIndex->setForeignKey(true);
126
127 detailsField = field2;
128 m_detailsIndex = new IndexSchema(detailsField->table());
129 m_detailsIndexOwned = true;
130 m_detailsIndex->addField(detailsField);
131 m_detailsIndex->setForeignKey(true);
132 }
133
134 if (!m_masterIndex || !m_detailsIndex)
135 return; //failed
136
137 setIndices(m_masterIndex, m_detailsIndex, false);
138}
139
140TableSchema* Relationship::masterTable() const
141{
142 return m_masterIndex ? m_masterIndex->table() : 0;
143}
144
145TableSchema* Relationship::detailsTable() const
146{
147 return m_detailsIndex ? m_detailsIndex->table() : 0;
148}
149
150void Relationship::setIndices(IndexSchema* masterIndex, IndexSchema* detailsIndex)
151{
152 setIndices(masterIndex, detailsIndex, true);
153}
154
155void Relationship::setIndices(IndexSchema* masterIndex, IndexSchema* detailsIndex, bool ownedByMaster)
156{
157 m_masterIndex = 0;
158 m_detailsIndex = 0;
159 m_pairs.clear();
160 if (!masterIndex || !detailsIndex || !masterIndex->table() || !detailsIndex->table()
161 || masterIndex->table()==detailsIndex->table() || masterIndex->fieldCount()!=detailsIndex->fieldCount())
162 return;
163 Field::ListIterator it1(*masterIndex->fields());
164 Field::ListIterator it2(*detailsIndex->fields());
165 for (;it1.current() && it1.current(); ++it1, ++it2) {
166 Field *f1 = it1.current(); //masterIndex->fields()->first();
167 Field *f2 = it2.current(); //detailsIndex->fields()->first();
168 // while (f1 && f2) {
169 if (f1->type()!=f1->type() && f1->isIntegerType()!=f2->isIntegerType() && f1->isTextType()!=f2->isTextType()) {
170 KexiDBWarn << "Relationship::setIndices(INDEX on '"<<masterIndex->table()->name()
171 <<"',INDEX on "<<detailsIndex->table()->name()<<"): !equal field types: "
172 <<Driver::defaultSQLTypeName(f1->type())<<" "<<f1->name()<<", "
173 <<Driver::defaultSQLTypeName(f2->type())<<" "<<f2->name() <<endl;
174 m_pairs.clear();
175 return;
176 }
177#if 0 //too STRICT!
178 if ((f1->isUnsigned() && !f2->isUnsigned()) || (!f1->isUnsigned() && f1->isUnsigned())) {
179 KexiDBWarn << "Relationship::setIndices(INDEX on '"<<masterIndex->table()->name()
180 <<"',INDEX on "<<detailsIndex->table()->name()<<"): !equal signedness of field types: "
181 <<Driver::defaultSQLTypeName(f1->type())<<" "<<f1->name()<<", "
182 <<Driver::defaultSQLTypeName(f2->type())<<" "<<f2->name() <<endl;
183 m_pairs.clear();
184 return;
185 }
186#endif
187 m_pairs.append( new Field::Pair(f1,f2) );
188 }
189 //ok: update information
190 if (m_masterIndex) {//detach yourself
191 m_masterIndex->detachRelationship(this);
192 }
193 if (m_detailsIndex) {//detach yourself
194 m_detailsIndex->detachRelationship(this);
195 }
196 m_masterIndex = masterIndex;
197 m_detailsIndex = detailsIndex;
198 m_masterIndex->attachRelationship(this, ownedByMaster);
199 m_detailsIndex->attachRelationship(this, ownedByMaster);
200}
201
static TQString defaultSQLTypeName(int id_t)
Definition driver.cpp:206
Meta-data for a field.
Definition field.h:73
bool isPrimaryKey() const
Definition field.h:234
TQPair< Field *, Field * > Pair
fields pair
Definition field.h:78
Type type() const
Definition field.cpp:134
bool isIntegerType() const
Definition field.h:259
TQPtrListIterator< Field > ListIterator
iterator for list of fields
Definition field.h:77
virtual bool isQueryAsterisk() const
Definition field.h:510
bool isTextType() const
Definition field.h:280
virtual TableSchema * table() const
Definition field.cpp:300
bool isUnsigned() const
if the type has the unsigned attribute
Definition field.h:384
Provides information about database index that can be created for a database table.
Definition indexschema.h:44
KexiDB::QuerySchema provides information about database query.
bool contains(TableSchema *table) const
IndexSchema * primaryKey() const
Definition tableschema.h:76
High-level database connectivity library with database backend drivers.