KDb

KDbTableSchemaChangeListener.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 "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 
32 class KDbTableSchemaChangeListenerPrivate
33 {
34 public:
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) {
48  listeners = new QSet<KDbTableSchemaChangeListener*>();
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) {
65  listeners = new QSet<KDbTableSchemaChangeListener*>();
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 
351 KDbTableSchemaChangeListener::KDbTableSchemaChangeListener()
352  : d(new KDbTableSchemaChangeListenerPrivate)
353 {
354 }
355 
356 KDbTableSchemaChangeListener::~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.toList();
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.toList();
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 : 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 : toClose) {
567  const tristate localResult = listener->closeListener();
568  if (localResult != true) {
569  result = localResult;
570  }
571  }
572  return result;
573 }
Provides information about lookup field's setup.
bool remove(const T &value)
An interface allowing to listen for table schema changes.
static void unregisterForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Unregisters listener for receiving (listening) information about changes in table schema table.
@ Query
named query as lookup record source
QString name
QList< T > toList() const const
QList< KDbTableSchema * > * tables() const
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
static QList< KDbTableSchemaChangeListener * > listeners(KDbConnection *conn, const KDbTableSchema *table)
QSet< T > toSet() const const
KDbQuerySchema * querySchema(int queryId)
@ Table
table as lookup record source
3-state logical type with three values: true, false and cancelled and convenient operators.
Definition: KDbTristate.h:100
KDbTableSchema * tableSchema(int tableId)
bool contains(const T &value) const const
KDbQuerySchema provides information about database query.
void clear()
QString fromLatin1(const char *str, int size)
QSet< T > & unite(const QSet< T > &other)
QString name(StandardShortcut id)
virtual tristate closeListener()=0
Closes listening object so it will be deleted and thus no longer use a conflicting table schema.
QSet::iterator insert(const T &value)
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.
Provides database connection, allowing queries and data modification.
Definition: KDbConnection.h:51
static void registerForChanges(KDbConnection *conn, KDbTableSchemaChangeListener *listener, const KDbTableSchema *table)
Registers listener for receiving (listening) information about changes in table schema table and all ...
QVector< KDbLookupFieldSchema * > lookupFields() const
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.