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.
QList< KDbTableSchema * > * tables() const
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)
QVector< KDbLookupFieldSchema * > lookupFields() const
3-state logical type with three values: true, false and cancelled and convenient operators.
std::optional< QSqlQuery > query(const QString &queryStatement)
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 Sat Dec 21 2024 17:00:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.