KDb

KDbTableSchemaChangeListener.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 "KDbTableSchemaChangeListener.h"
21#include "KDbConnection.h"
22#include "KDbConnection_p.h"
23#include "KDbLookupFieldSchema.h"
24#include "kdb_debug.h"
25
26#ifdef KDB_TABLESCHEMACHANGELISTENER_DEBUG
27# define localDebug(...) kdbDebug(__VA_ARGS__)
28#else
29# define localDebug(...) if (true) {} else kdbDebug(__VA_ARGS__)
30#endif
31
32class KDbTableSchemaChangeListenerPrivate
33{
34public:
35 KDbTableSchemaChangeListenerPrivate()
36 {
37 }
38
39 //! Registers listener @a listener for changes in table @a table
40 static void registerForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener,
41 const KDbTableSchema *table)
42 {
43 Q_ASSERT(conn);
44 Q_ASSERT(listener);
45 Q_ASSERT(table);
46 QSet<KDbTableSchemaChangeListener*>* listeners = conn->d->tableSchemaChangeListeners.value(table);
47 if (!listeners) {
49 conn->d->tableSchemaChangeListeners.insert(table, listeners);
50 }
51 localDebug() << "listener=" << listener << listener->name() << "table=" << table << table->name();
52 listeners->insert(listener);
53 }
54
55 //! Registers listener @a listener for changes in query @a query
56 static void registerForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener,
57 const KDbQuerySchema *query)
58 {
59 Q_ASSERT(conn);
60 Q_ASSERT(listener);
61 Q_ASSERT(query);
63 = conn->d->queryTableSchemaChangeListeners.value(query);
64 if (!listeners) {
66 conn->d->queryTableSchemaChangeListeners.insert(query, listeners);
67 }
68 localDebug() << "listener=" << listener->name() << "query=" << query->name();
69 listeners->insert(listener);
70 }
71
72 //! Unregisters listener @a listener for changes in table @a table
73 static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener,
74 const KDbTableSchema *table)
75 {
76 Q_ASSERT(conn);
77 Q_ASSERT(table);
79 = conn->d->tableSchemaChangeListeners.value(table);
80 if (!listeners) {
81 return;
82 }
83 localDebug() << "listener=" << listener << (listener ? listener->name() : QString::fromLatin1("<all>"))
84 << "table=" << table << table->name();
85 if (listener) {
86 listeners->remove(listener);
87 } else {
88 delete conn->d->tableSchemaChangeListeners.take(table);
89 }
90 }
91
92 //! Unregisters listener @a listener for changes in query @a query
93 static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener,
94 const KDbQuerySchema *query)
95 {
96 Q_ASSERT(conn);
97 Q_ASSERT(query);
99 = conn->d->queryTableSchemaChangeListeners.value(query);
100 if (!listeners) {
101 return;
102 }
103 localDebug() << "listener=" << (listener ? listener->name() : QString::fromLatin1("<all>"))
104 << "query=" << query->name();
105 if (listener) {
106 listeners->remove(listener);
107 } else {
108 listeners->clear();
109 }
110 }
111
112 //! Unregisters listener @a listener for any changes
113 static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener* listener)
114 {
115 Q_ASSERT(conn);
116 Q_ASSERT(listener);
117 localDebug() << "listener=" << listener->name();
118 for (QSet<KDbTableSchemaChangeListener*> *listeners : conn->d->tableSchemaChangeListeners) {
119 listeners->remove(listener);
120 }
121 for (QSet<KDbTableSchemaChangeListener*> *listeners : conn->d->queryTableSchemaChangeListeners) {
122 listeners->remove(listener);
123 }
124 }
125
126 //! Returns @c true if @a table1 depends on @a table2, that is, if:
127 //! - @a table1 == @a table2, or
128 //! - @a table1 has lookup columns that reference @a table2
129 static bool tableDependsOnTable(QSet<const KDbTableSchema *> *checkedTables,
130 QSet<const KDbQuerySchema *> *checkedQueries,
131 KDbConnection *conn, const KDbTableSchema *table1,
132 const KDbTableSchema *table2)
133 {
134 if (checkedTables->contains(table1)) {
135 localDebug() << "Table" << table1 << table1->name() << "already checked";
136 return false; // protection against infinite recursion
137 }
138 checkedTables->insert(table1);
139 localDebug() << "Checking if table" << table1 << table1->name() << "depends on table"
140 << table2 << table2->name();
141 if (table1 == table2) {
142 localDebug() << "Yes";
143 return true;
144 }
145 for (KDbLookupFieldSchema *lookup : table1->lookupFields()) {
146 switch (lookup->recordSource().type()) {
148 const KDbTableSchema *sourceTable
149 = conn->tableSchema(lookup->recordSource().name());
150 if (sourceTable
151 && tableDependsOnTable(checkedTables, checkedQueries, conn, sourceTable, table2))
152 {
153 return true;
154 }
155 break;
156 }
158 const KDbQuerySchema *sourceQuery
159 = conn->querySchema(lookup->recordSource().name());
160 if (sourceQuery
161 && queryDependsOnTable(checkedTables, checkedQueries, conn, sourceQuery, table2))
162 {
163 return true;
164 }
165 break;
166 }
167 default:
168 kdbWarning() << "Unsupported lookup field's source type" << lookup->recordSource().typeName();
169 //! @todo support more record source types
170 break;
171 }
172 }
173 localDebug() << "No";
174 return false;
175 }
176
177 //! Returns @c true if @a table depends on @a query, that is, if:
178 //! - @a table has lookup columns that reference @a query
179 static bool tableDependsOnQuery(QSet<const KDbTableSchema *> *checkedTables,
180 QSet<const KDbQuerySchema *> *checkedQueries,
181 KDbConnection *conn, const KDbTableSchema *table,
182 const KDbQuerySchema *query)
183 {
184 if (checkedTables->contains(table)) {
185 localDebug() << "Table" << table->name() << "already checked";
186 return false; // protection against infinite recursion
187 }
188 checkedTables->insert(table);
189 localDebug() << "Checking if table" << table->name() << "depends on query" << query->name();
190 for (KDbLookupFieldSchema *lookup : table->lookupFields()) {
191 switch (lookup->recordSource().type()) {
193 const KDbTableSchema *sourceTable
194 = conn->tableSchema(lookup->recordSource().name());
195 if (sourceTable
196 && tableDependsOnQuery(checkedTables, checkedQueries, conn, sourceTable, query))
197 {
198 return true;
199 }
200 break;
201 }
203 const KDbQuerySchema *sourceQuery
204 = conn->querySchema(lookup->recordSource().name());
205 if (sourceQuery
206 && queryDependsOnQuery(checkedTables, checkedQueries, conn, sourceQuery, query))
207 {
208 return true;
209 }
210 break;
211 }
212 default:
213 kdbWarning() << "Unsupported lookup field's source type" << lookup->recordSource().typeName();
214 //! @todo support more record source types
215 }
216 }
217 return false;
218 }
219
220 //! Returns @c true if @a query depends on @a table, that is, if:
221 //! - @a query references table that depends on @a table (dependency is checked using
222 //! tableDependsOnTable())
223 static bool queryDependsOnTable(QSet<const KDbTableSchema *> *checkedTables,
224 QSet<const KDbQuerySchema *> *checkedQueries,
225 KDbConnection *conn, const KDbQuerySchema *query,
226 const KDbTableSchema *table)
227 {
228 if (checkedQueries->contains(query)) {
229 localDebug() << "Query" << query->name() << "already checked";
230 return false; // protection against infinite recursion
231 }
232 checkedQueries->insert(query);
233 localDebug() << "Checking if query" << query->name() << "depends on table" << table->name();
234 for (const KDbTableSchema *queryTable : *query->tables()) {
235 if (tableDependsOnTable(checkedTables, checkedQueries, conn, queryTable, table)) {
236 return true;
237 }
238 }
239 return false;
240 }
241
242 //! Returns @c true if @a query1 depends on @a query2, that is, if:
243 //! - @a query1 == @a query2, or
244 //! - @a query2 references table that depends on @a query (dependency is checked using
245 //! tableDependsOnQuery())
246 static bool queryDependsOnQuery(QSet<const KDbTableSchema *> *checkedTables,
247 QSet<const KDbQuerySchema *> *checkedQueries,
248 KDbConnection *conn, const KDbQuerySchema *query1,
249 const KDbQuerySchema *query2)
250 {
251 if (checkedQueries->contains(query1)) {
252 localDebug() << "Query" << query1->name() << "already checked";
253 return false; // protection against infinite recursion
254 }
255 checkedQueries->insert(query1);
256 localDebug() << "Checking if query" << query1->name() << "depends on query" << query2->name();
257 if (query1 == query2) {
258 localDebug() << "Yes";
259 return true;
260 }
261 for (const KDbTableSchema *queryTable : *query1->tables()) {
262 if (tableDependsOnQuery(checkedTables, checkedQueries, conn, queryTable, query2)) {
263 return true;
264 }
265 }
266 return false;
267 }
268
269 //! Inserts to @a *result all listeners that listen to changes in table @a table and other tables
270 //! or queries depending on @a table.
271 static void collectListeners(QSet<KDbTableSchemaChangeListener *> *result,
272 KDbConnection *conn,
273 const KDbTableSchema *table)
274 {
275 Q_ASSERT(result);
276 Q_ASSERT(conn);
277 Q_ASSERT(table);
278 // for all tables with listeners:
279 for (QHash<const KDbTableSchema*, QSet<KDbTableSchemaChangeListener*>* >::ConstIterator it(
280 conn->d->tableSchemaChangeListeners.constBegin());
281 it != conn->d->tableSchemaChangeListeners.constEnd(); ++it)
282 {
283 // check if it depends on our table
284 QSet<const KDbTableSchema *> checkedTables;
285 QSet<const KDbQuerySchema *> checkedQueries;
286 if (tableDependsOnTable(&checkedTables, &checkedQueries, conn, it.key(), table)) {
287 QSet<KDbTableSchemaChangeListener*>* set = it.value();
288 result->unite(*set);
289 }
290 }
291 // for all queries with listeners:
292 for (QHash<const KDbQuerySchema*, QSet<KDbTableSchemaChangeListener*>* >::ConstIterator it(
293 conn->d->queryTableSchemaChangeListeners.constBegin());
294 it != conn->d->queryTableSchemaChangeListeners.constEnd(); ++it)
295 {
296 // check if it depends on our table
297 QSet<const KDbTableSchema *> checkedTables;
298 QSet<const KDbQuerySchema *> checkedQueries;
299 if (queryDependsOnTable(&checkedTables, &checkedQueries, conn, it.key(), table)) {
300 QSet<KDbTableSchemaChangeListener*>* set = it.value();
301 result->unite(*set);
302 }
303 }
304 }
305
306 //! Inserts to @a *result all listeners that listen to changes in query @a table and other tables
307 //! or queries depending on @a query.
308 static void collectListeners(QSet<KDbTableSchemaChangeListener *> *result,
309 KDbConnection *conn,
310 const KDbQuerySchema *query)
311 {
312 Q_ASSERT(result);
313 Q_ASSERT(conn);
314 Q_ASSERT(query);
315 QSet<KDbTableSchemaChangeListener*>* set = conn->d->queryTableSchemaChangeListeners.value(query);
316 if (set) {
317 result->unite(*set);
318 }
319 // for all tables with listeners:
320 for (QHash<const KDbTableSchema*, QSet<KDbTableSchemaChangeListener*>* >::ConstIterator it(
321 conn->d->tableSchemaChangeListeners.constBegin());
322 it != conn->d->tableSchemaChangeListeners.constEnd(); ++it)
323 {
324 // check if it depends on our query
325 QSet<const KDbTableSchema *> checkedTables;
326 QSet<const KDbQuerySchema *> checkedQueries;
327 if (tableDependsOnQuery(&checkedTables, &checkedQueries, conn, it.key(), query)) {
328 QSet<KDbTableSchemaChangeListener*>* set = it.value();
329 result->unite(*set);
330 }
331 }
332 // for all queries with listeners:
333 for (QHash<const KDbQuerySchema*, QSet<KDbTableSchemaChangeListener*>* >::ConstIterator it(
334 conn->d->queryTableSchemaChangeListeners.constBegin());
335 it != conn->d->queryTableSchemaChangeListeners.constEnd(); ++it)
336 {
337 // check if it depends on our query
338 QSet<const KDbTableSchema *> checkedTables;
339 QSet<const KDbQuerySchema *> checkedQueries;
340 if (queryDependsOnQuery(&checkedTables, &checkedQueries, conn, it.key(), query)) {
341 QSet<KDbTableSchemaChangeListener*>* set = it.value();
342 result->unite(*set);
343 }
344 }
345 }
346
347 QString name;
348 Q_DISABLE_COPY(KDbTableSchemaChangeListenerPrivate)
349};
350
351KDbTableSchemaChangeListener::KDbTableSchemaChangeListener()
352 : d(new KDbTableSchemaChangeListenerPrivate)
353{
354}
355
356KDbTableSchemaChangeListener::~KDbTableSchemaChangeListener()
357{
358 delete d;
359}
360
362{
363 return d->name;
364}
365
367{
368 d->name = name;
369}
370
371// static
374 const KDbTableSchema* table)
375{
376 if (!conn) {
377 kdbWarning() << "Missing connection";
378 return;
379 }
380 if (!listener) {
381 kdbWarning() << "Missing listener";
382 return;
383 }
384 if (!table) {
385 kdbWarning() << "Missing table";
386 return;
387 }
388 KDbTableSchemaChangeListenerPrivate::registerForChanges(conn, listener, table);
389}
390
391// static
394 const KDbQuerySchema *query)
395{
396 if (!conn) {
397 kdbWarning() << "Missing connection";
398 return;
399 }
400 if (!listener) {
401 kdbWarning() << "Missing listener";
402 return;
403 }
404 if (!query) {
405 kdbWarning() << "Missing query";
406 return;
407 }
408 KDbTableSchemaChangeListenerPrivate::registerForChanges(conn, listener, query);
409}
410
411// static
414 const KDbTableSchema* table)
415{
416 if (!conn) {
417 kdbWarning() << "Missing connection";
418 return;
419 }
420 if (!listener) {
421 kdbWarning() << "Missing listener";
422 return;
423 }
424 if (!table) {
425 kdbWarning() << "Missing table";
426 return;
427 }
428 KDbTableSchemaChangeListenerPrivate::unregisterForChanges(conn, listener, table);
429}
430
431// static
433 const KDbTableSchema* table)
434{
435 if (!conn) {
436 kdbWarning() << "Missing connection";
437 return;
438 }
439 if (!table) {
440 kdbWarning() << "Missing table";
441 return;
442 }
443 KDbTableSchemaChangeListenerPrivate::unregisterForChanges(conn, nullptr, table);
444}
445
448 const KDbQuerySchema *query)
449{
450 if (!conn) {
451 kdbWarning() << "Missing connection";
452 return;
453 }
454 if (!listener) {
455 kdbWarning() << "Missing listener";
456 return;
457 }
458 if (!query) {
459 kdbWarning() << "Missing query";
460 return;
461 }
462 KDbTableSchemaChangeListenerPrivate::unregisterForChanges(conn, listener, query);
463}
464
465// static
467 const KDbQuerySchema *query)
468{
469 if (!conn) {
470 kdbWarning() << "Missing connection";
471 return;
472 }
473 if (!query) {
474 kdbWarning() << "Missing query";
475 return;
476 }
477 KDbTableSchemaChangeListenerPrivate::unregisterForChanges(conn, nullptr, query);
478}
479
480// static
483{
484 if (!conn) {
485 kdbWarning() << "Missing connection";
486 return;
487 }
488 if (!listener) {
489 kdbWarning() << "Missing listener";
490 return;
491 }
492 KDbTableSchemaChangeListenerPrivate::unregisterForChanges(conn, listener);
493}
494
495// static
497 KDbConnection *conn, const KDbTableSchema* table)
498{
499 if (!conn) {
500 kdbWarning() << "Missing connection";
502 }
503 if (!table) {
504 kdbWarning() << "Missing table";
506 }
508 KDbTableSchemaChangeListenerPrivate::collectListeners(&result, conn, table);
509 return result.values();
510}
511
512// static
514 KDbConnection *conn, const KDbQuerySchema *query)
515{
516 if (!conn) {
517 kdbWarning() << "Missing connection";
519 }
520 if (!query) {
521 kdbWarning() << "Missing query";
523 }
525 KDbTableSchemaChangeListenerPrivate::collectListeners(&result, conn, query);
526 return result.values();
527}
528
529// static
531 const KDbTableSchema *table, const QList<KDbTableSchemaChangeListener *> &except)
532{
533 if (!conn) {
534 kdbWarning() << "Missing connection";
535 return false;
536 }
537 if (!table) {
538 kdbWarning() << "Missing table";
539 return false;
540 }
541 QSet<KDbTableSchemaChangeListener*> toClose(listeners(conn, table).toSet().subtract(except.toSet()));
542 tristate result = true;
543 for (KDbTableSchemaChangeListener *listener : qAsConst(toClose)) {
544 const tristate localResult = listener->closeListener();
545 if (localResult != true) {
546 result = localResult;
547 }
548 }
549 return result;
550}
551
552// static
554 const KDbQuerySchema *query, const QList<KDbTableSchemaChangeListener *> &except)
555{
556 if (!conn) {
557 kdbWarning() << "Missing connection";
558 return false;
559 }
560 if (!query) {
561 kdbWarning() << "Missing query";
562 return false;
563 }
564 QSet<KDbTableSchemaChangeListener*> toClose(listeners(conn, query).toSet().subtract(except.toSet()));
565 tristate result = true;
566 for (KDbTableSchemaChangeListener *listener : qAsConst(toClose)) {
567 const tristate localResult = listener->closeListener();
568 if (localResult != true) {
569 result = localResult;
570 }
571 }
572 return result;
573}
Provides database connection, allowing queries and data modification.
KDbTableSchema * tableSchema(int tableId)
KDbQuerySchema * querySchema(int queryId)
@ Table
table as lookup record source
@ Query
named query as lookup record source
Provides information about lookup field's setup.
KDbQuerySchema provides information about database query.
An interface allowing to listen for table schema changes.
virtual tristate closeListener()=0
Closes listening object so it will be deleted and thus no longer use a conflicting table schema.
static tristate closeListeners(KDbConnection *conn, const KDbTableSchema *table, const QList< KDbTableSchemaChangeListener * > &except=QList< KDbTableSchemaChangeListener * >())
Closes all table schema listeners for table schema table except for the ones from the except list.
static void registerForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Registers listener for receiving (listening) information about changes in table schema table and all ...
static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Unregisters listener for receiving (listening) information about changes in table schema table.
static QList< KDbTableSchemaChangeListener * > listeners(KDbConnection *conn, const KDbTableSchema *table)
3-state logical type with three values: true, false and cancelled and convenient operators.
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
void clear()
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
bool remove(const T &value)
QSet< T > & unite(const QSet< T > &other)
QList< T > values() const const
QString fromLatin1(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:59 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.