Kstars

catalogsdb.h
1 /*
2  SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include <QSqlDatabase>
10 #include <QSqlError>
11 #include <exception>
12 #include <list>
13 #include <QString>
14 #include <QList>
15 #include <catalogsdb_debug.h>
16 #include <QSqlQuery>
17 #include <QMutex>
18 #include <QObject>
19 #include <QThread>
20 
21 #include "polyfills/qstring_hash.h"
22 #include <unordered_map>
23 #include <queue>
24 
25 #include <unordered_set>
26 #include <utility>
27 #include "catalogobject.h"
28 #include "nan.h"
29 #include "typedef.h"
30 
31 namespace CatalogsDB
32 {
33 /**
34  * A simple struct to hold information about catalogs.
35  */
36 struct Catalog
37 {
38  /**
39  * The catalog id.
40  */
41  int id = -1;
42 
43  /**
44  * The catalog mame.
45  */
46  QString name = "Unknown";
47 
48  /**
49  * The precedence level of a catalog.
50  *
51  * If doublicate objects exist in the database, the one from the
52  * catalog with the highest precedence winns.
53  */
54  double precedence = 0;
55 
56  /**
57  * The author of the catalog.
58  */
60 
61  /**
62  * The catalog source.
63  */
65 
66  /**
67  * A (short) description for the catalog.
68  * QT html is allowed.
69  */
71 
72  /**
73  * Wether the catalog is mutable.
74  */
75  bool mut = false;
76 
77  /**
78  * Wether the catalog is enabled.
79  */
80  bool enabled = false;
81 
82  /**
83  * The catalog version.
84  */
85  int version = -1;
86 
87  /**
88  * The catalog color in the form `[default color];[scheme file
89  * name];[color]...`.
90  */
91  QString color = "";
92 
93  /**
94  * The catalog license.
95  */
97 
98  /**
99  * The catalog maintainer.
100  */
102 
103  /**
104  * Build time of the catalog. Usually only catalogs with the same
105  * timestamp can be considered dedublicated.
106  *
107  * A `null` timestamp indicates that the catalog has not been
108  * built by the catalog repository.
109  */
111 };
112 
113 const Catalog cat_defaults{};
114 
115 /**
116  * Holds statistical information about the objects in a catalog.
117  */
119 {
120  std::map<SkyObject::TYPE, int> object_counts;
121  int total_count = 0;
122 };
123 
124 const QString db_file_extension = "kscat";
125 constexpr int application_id = 0x4d515158;
126 constexpr int custom_cat_min_id = 1000;
127 constexpr int user_catalog_id = 0;
128 constexpr float default_maglim = 99;
129 const QString flux_unit = "mag";
130 const QString flux_frequency = "400 nm";
131 using CatalogColorMap = std::map<QString, QColor>;
132 using ColorMap = std::map<int, CatalogColorMap>;
133 using CatalogObjectList = std::list<CatalogObject>;
134 using CatalogObjectVector = std::vector<CatalogObject>;
135 
136 /**
137  * \returns A hash table of the form `color scheme: color` by
138  * parsing a string of the form `[default color];[scheme file
139  * name];[color]...`.
140  */
141 CatalogColorMap parse_color_string(const QString &str);
142 
143 /**
144  * \returns A color string of the form`[default color];[scheme file
145  * name];[color]...`.
146  *
147  * The inverse of `CatalogsDB::parse_color_string`.
148  */
149 QString to_color_string(CatalogColorMap colors);
150 
151 /**
152  * Manages the catalog database and provides an interface to provide an
153  * interface to query and modify the database. For more information on
154  * how the catalog database system works see the KStars Handbook.
155  *
156  * The class manages a database connection which is assumed to be
157  * working (invariant). If the database can't be accessed a
158  * DatabaseError is thrown upon construction. The manager is designed
159  * to hold as little state as possible because the database should be
160  * the single source of truth. Prepared statements are made class
161  * members, only if they are performance critical.
162  *
163  * Most methods in this class are thread safe.
164  *
165  * The intention is that you access a/the catalogs database directly
166  * locally in the code where objects from the database are required and
167  * not through layers of references and pointers.
168  *
169  * The main DSO database can be accessed as follows:
170  * ```cpp
171  * CatalogsDB::DBManager manager{ CatalogsDB::dso_db_path() };
172  * for(auto& o : manager.get_objects(10)) {
173  * // do something
174  * }
175  * ```
176  *
177  * To query the database, first check if the required query is already
178  * hardcoded into the `DBManager`. If this is not the case you can either
179  * add it (if it is performance critical and executed frequently) or use
180  * `DBManager::general_master_query` to construct a custom `SQL` query.
181  */
183 {
184  public:
185  /**
186  * Constructs a database manager from the \p filename which is
187  * resolved to a path in the kstars data directory.
188  *
189  * The constructor resolves the path to the database, opens it
190  * (throws if that does not work), checks the database version
191  * (throws if that does not match), initializes the database,
192  * registers the user catalog and updates the all_catalog_view.
193  */
194  DBManager(const QString &filename);
195  DBManager(const DBManager &other);
196 
197  DBManager &operator=(DBManager other)
198  {
199  using std::swap;
200  DBManager tmp{ other };
201 
202  m_db_file = other.m_db_file;
203  swap(m_db, other.m_db);
204  swap(m_q_cat_by_id, other.m_q_cat_by_id);
205  swap(m_q_obj_by_trixel, other.m_q_obj_by_trixel);
206  swap(m_q_obj_by_trixel_no_nulls, other.m_q_obj_by_trixel_no_nulls);
207  swap(m_q_obj_by_trixel_null_mag, other.m_q_obj_by_trixel_null_mag);
208  swap(m_q_obj_by_name, other.m_q_obj_by_name);
209  swap(m_q_obj_by_name_exact, other.m_q_obj_by_name_exact);
210  swap(m_q_obj_by_lim, other.m_q_obj_by_lim);
211  swap(m_q_obj_by_maglim, other.m_q_obj_by_maglim);
212  swap(m_q_obj_by_maglim_and_type, other.m_q_obj_by_maglim_and_type);
213  swap(m_q_obj_by_oid, other.m_q_obj_by_oid);
214 
215  return *this;
216  };
217 
218  ~DBManager()
219  {
220  m_db.commit();
221  m_db.close();
222  }
223 
224  /**
225  * @return the filename of the database
226  */
227  const QString &db_file_name() const { return m_db_file; };
228 
229  /**
230  * @return wether the catalog with the \p id has been found and
231  * the catalog.
232  *
233  * @todo use std::optional when transitioning to c++17
234  */
235  const std::pair<bool, Catalog> get_catalog(const int id);
236 
237  /**
238  * \return a vector with all catalogs from the database. If \p
239  * include_disabled is `true`, disabled catalogs will be included.
240  */
241  const std::vector<Catalog> get_catalogs(bool include_disabled = false);
242 
243  /**
244  * @return true if the catalog with \p id exists
245  *
246  * @todo use std::optional when transitioning to c++17
247  */
248  bool catalog_exists(const int id);
249 
250  /**
251  * @return return a vector of objects in the trixel with \p id.
252  */
253  inline CatalogObjectVector get_objects_in_trixel(const int trixel) {
254  return _get_objects_in_trixel_generic(m_q_obj_by_trixel, trixel);
255  }
256 
257  /**
258  * @return return a vector of objects of known mag in the trixel with \p id.
259  */
260  inline CatalogObjectVector get_objects_in_trixel_no_nulls(const int trixel) {
261  return _get_objects_in_trixel_generic(m_q_obj_by_trixel_no_nulls, trixel);
262  }
263 
264  /**
265  * @return return a vector of objects of unknown mag in the trixel with \p id.
266  */
267  inline CatalogObjectVector get_objects_in_trixel_null_mag(const int trixel) {
268  return _get_objects_in_trixel_generic(m_q_obj_by_trixel_null_mag, trixel);
269  }
270 
271  /**
272  * \brief Find an objects by name.
273  *
274  * This will search the `name`, `long_name` and `catalog_identifier`
275  * fields in all enabled catalogs for \p `name` and then return a new
276  * instance of `CatalogObject` sourced from the master catalog.
277  *
278  * \param limit Upper limit to the quanitity of results. `-1` means "no
279  * limit"
280  * \param exactMatchOnly If true, the supplied name must match exactly
281  *
282  * \return a list of matching objects
283  */
284  CatalogObjectList find_objects_by_name(const QString &name, const int limit = -1,
285  const bool exactMatchOnly = false);
286 
287  /**
288  * \brief Find an objects by name in the catalog with \p `catalog_id`.
289  *
290  * \return a list of matching objects
291  */
292  CatalogObjectList find_objects_by_name(const int catalog_id, const QString &name,
293  const int limit = -1);
294 
295  /**
296  * \brief Find an objects by searching the name four wildcard. See
297  * the LIKE sqlite statement.
298  *
299  * \return a list of matching objects
300  */
301  CatalogObjectList find_objects_by_wildcard(const QString &wildcard,
302  const int limit = -1);
303  /**
304  * \brief Find an objects by searching the master catlog with a
305  * query like `SELECT ... FROM master WHERE \p where ORDER BY \p
306  * order_by ...`.
307  *
308  * To be used if performance does not matter (much).
309  * \p order_by can be ommitted.
310  *
311  * \return wether the query was successful, an error message if
312  * any and a list of matching objects
313  */
314  std::tuple<bool, const QString, CatalogObjectList>
315  general_master_query(const QString &where, const QString &order_by = "",
316  const int limit = -1);
317 
318  /**
319  * \brief Get an object by \p `oid`. Optinally a \p `catalog_id` can be speicfied.
320  *
321  * \returns if the object was found and the object itself
322  */
323  std::pair<bool, CatalogObject> get_object(const CatalogObject::oid &oid);
324  std::pair<bool, CatalogObject> get_object(const CatalogObject::oid &oid,
325  const int catalog_id);
326 
327  /**
328  * Get \p limit objects with magnitude smaller than \p maglim (smaller =
329  * brighter) from the database.
330  */
331  CatalogObjectList get_objects(float maglim = default_maglim, int limit = -1);
332 
333  /**
334  * Get all objects from the database.
335  */
336  CatalogObjectList get_objects_all();
337 
338  /**
339  * Get \p limit objects of \p type with magnitude smaller than \p
340  * maglim (smaller = brighter) from the database. Optionally one
341  * can filter by \p `catalog_id`.
342  */
343  CatalogObjectList get_objects(SkyObject::TYPE type, float maglim = default_maglim,
344  int limit = -1);
345  /**
346  * Get \p limit objects from the catalog with \p `catalog_id` of
347  * \p type with magnitude smaller than \p maglim (smaller =
348  * brighter) from the database. Optionally one can filter by \p
349  * `catalog_id`.
350  */
351  CatalogObjectList get_objects_in_catalog(SkyObject::TYPE type, const int catalog_id,
352  float maglim = default_maglim,
353  int limit = -1);
354 
355  /**
356  * @return return the htmesh level used by the catalog db
357  */
358  int htmesh_level() const { return m_htmesh_level; };
359 
360  /**
361  * \brief Enable or disable a catalog.
362  * \return `true` in case of succes, `false` and an error message in case
363  * of an error
364  *
365  * This will recreate the master table.
366  */
367  std::pair<bool, QString> set_catalog_enabled(const int id, const bool enabled);
368 
369  /**
370  * \brief remove a catalog
371  * \return `true` in case of succes, `false` and an error message
372  * in case of an error
373  *
374  * This will recreate the master table.
375  */
376  std::pair<bool, QString> remove_catalog(const int id);
377 
378  /**
379  * Add a `CatalogObject` to a table with \p `catalog_id`. For the rest of
380  * the arguments see `CatalogObject::CatalogObject`.
381  *
382  * \returns wether the operation was successful and if not, an error
383  * message
384  */
385  std::pair<bool, QString> add_object(const int catalog_id, const SkyObject::TYPE t,
386  const CachingDms &r, const CachingDms &d,
387  const QString &n, const float m = NaN::f,
388  const QString &lname = QString(),
389  const QString &catalog_identifier = QString(),
390  const float a = 0.0, const float b = 0.0,
391  const double pa = 0.0, const float flux = 0);
392 
393  /**
394  * Add the \p `object` to a table with \p `catalog_id`. For the
395  * rest of the arguments see `CatalogObject::CatalogObject`.
396  *
397  * \returns wether the operation was successful and if not, an
398  * error message
399  */
400  std::pair<bool, QString> add_object(const int catalog_id, const CatalogObject &obj);
401 
402  /**
403  * Add the \p `objects` to a table with \p `catalog_id`. For the
404  * rest of the arguments see `CatalogObject::CatalogObject`.
405  *
406  * \returns wether the operation was successful and if not, an
407  * error message
408  */
409  std::pair<bool, QString> add_objects(const int catalog_id,
410  const CatalogObjectVector &objects);
411 
412  /**
413  * Remove the catalog object with the \p `oid` from the catalog with the
414  * \p `catalog_id`.
415  *
416  * Refreshes the master catalog.
417  *
418  * \returns wether the operation was successful and if not, an
419  * error message
420  */
421  std::pair<bool, QString> remove_object(const int catalog_id,
422  const CatalogObject::oid &id);
423 
424  /**
425  * Dumps the catalog with \p `id` into the file under the path \p
426  * `file_path`. This file can then be imported with
427  * `import_catalog`. If the file already exists, it will be
428  * overwritten.
429  *
430  * The `user_version` and `application_id` pragmas are set to special
431  * values, but otherwise the dump format is equal to the internal
432  * database format.
433  *
434  * \returns wether the operation was successful and if not, an error
435  * message
436  */
437  std::pair<bool, QString> dump_catalog(int catalog_id, QString file_path);
438 
439  /**
440  * Loads a dumped catalog from path \p `file_path`. Will overwrite
441  * an existing catalog if \p `overwrite` is set to true. Immutable
442  * catalogs are overwritten by default.
443  *
444  * Checks if the pragma `application_id` matches
445  * `CatalogsDB::application_id` and the pragma `user_version` to match
446  * the database format version.
447  *
448  * \returns wether the operation was successful and if not, an error
449  * message
450  */
451  std::pair<bool, QString> import_catalog(const QString &file_path,
452  const bool overwrite = false);
453  /**
454  * Registers a new catalog in the database.
455  *
456  * For the parameters \sa Catalog. The catalog gets inserted into
457  * `m_catalogs`. The `all_catalog_view` is updated.
458  *
459  * \return true in case of success, false in case of an error
460  * (along with the error)
461  */
462  std::pair<bool, QString>
463  register_catalog(const int id, const QString &name, const bool mut,
464  const bool enabled, const double precedence,
465  const QString &author = cat_defaults.author,
466  const QString &source = cat_defaults.source,
467  const QString &description = cat_defaults.description,
468  const int version = cat_defaults.version,
469  const QString &color = cat_defaults.color,
470  const QString &license = cat_defaults.license,
471  const QString &maintainer = cat_defaults.maintainer,
472  const QDateTime &timestamp = cat_defaults.timestamp);
473 
474  std::pair<bool, QString> register_catalog(const Catalog &cat);
475 
476  /**
477  * Update the metatadata \p `catalog`.
478  *
479  * The updated fields are: title, author, source, description.
480  *
481  * \return true in case of success, false in case of an error
482  * (along with the error).
483  */
484  std::pair<bool, QString> update_catalog_meta(const Catalog &cat);
485 
486  /**
487  * Clone objects from the catalog with \p `id_1` to another with `id_2`. Useful to create a
488  * custom catalog from an immutable one.
489  */
490  std::pair<bool, QString> copy_objects(const int id_1, const int id_2);
491 
492  /**
493  * Finds the smallest free id for a catalog.
494  */
496 
497  /**
498  * \returns statistics about the master catalog.
499  */
500  const std::pair<bool, CatalogStatistics> get_master_statistics();
501 
502  /**
503  * \returns statistics about the catalog with \p `catalog_id`.
504  */
505  const std::pair<bool, CatalogStatistics> get_catalog_statistics(const int catalog_id);
506 
507  /**
508  * Compiles the master catalog by merging the individual catalogs based
509  * on `oid` and precedence and creates an index by (trixel, magnitude) on
510  * the master table. **Caution** you may want to call
511  * `update_catalog_views` beforhand.
512  *
513  * @return true in case of success, false in case of an error
514  */
515  bool compile_master_catalog();
516 
517  /**
518  * Updates the all_catalog_view so that it includes all known
519  * catalogs.
520  *
521  * @return true in case of success, false in case of an error
522  */
523  bool update_catalog_views();
524 
525  /** \returns the catalog colors as a hash table of the form `catalog id:
526  * scheme: color`.
527  *
528  * The colors are loaded from the `Catalog::color` field and the
529  * `SqlStatements::color_table` in that order.
530  */
531  ColorMap get_catalog_colors();
532 
533  /** \returns the catalog colors as a hash table of for the catalog
534  * with \p id in the form `scheme: color`.
535  *
536  * The colors are loaded from the `Catalog::color` field and the
537  * `SqlStatements::color_table` in that order.
538  */
539  CatalogColorMap get_catalog_colors(const int id);
540 
541  /** Saves the configures colors of the catalog with id \p id in \p
542  * colors into the database. \returns wether the insertion was
543  * possible and an error message if not.
544  */
545  std::pair<bool, QString> insert_catalog_colors(const int id,
546  const CatalogColorMap &colors);
547 
548  private:
549  /**
550  * The backing catalog database.
551  */
552  QSqlDatabase m_db;
553 
554  /**
555  * The filename of the database.
556  *
557  * Will be a reference to a member of `m_db_paths`.
558  */
559  std::reference_wrapper<const QString> m_db_file;
560 
561  //@{
562  /**
563  * Some performance criticall sql queries are prepared stored as memebers.
564  * When using those queries `m_mutex` should be locked!
565  *
566  * \sa prepare_queries
567  */
568 
569  QSqlQuery m_q_cat_by_id;
570  QSqlQuery m_q_obj_by_trixel;
571  QSqlQuery m_q_obj_by_trixel_null_mag;
572  QSqlQuery m_q_obj_by_trixel_no_nulls;
573  QSqlQuery m_q_obj_by_name;
574  QSqlQuery m_q_obj_by_name_exact;
575  QSqlQuery m_q_obj_by_lim;
576  QSqlQuery m_q_obj_by_maglim;
577  QSqlQuery m_q_obj_by_maglim_and_type;
578  QSqlQuery m_q_obj_by_oid;
579  //@}
580 
581  /**
582  * The level of the htmesh used to index the catalog entries.
583  *
584  * If the htmesh level of a catalog is different, the catalog will
585  * be reindexed upon importing it.
586  *
587  * A value of -1 means that the htmesh-level has not been
588  * deterined yet.
589  */
590  int m_htmesh_level = -1;
591 
592  /**
593  * The version of the database.
594  *
595  * A value of -1 means that the htmesh-level has not been
596  * deterined yet.
597  */
598  int m_db_version = -1;
599 
600  /**
601  * A simple mutex to be locked when using prepared statements,
602  * that are stored in the class.
603  */
604  QMutex m_mutex;
605 
606  //@{
607  /**
608  * Helpers
609  */
610 
611  /**
612  * Initializes the database with the minimum viable tables.
613  *
614  * The catalog registry is created and the database version is set
615  * to SqlStatements::current_db_version and the htmesh-level is
616  * set to SqlStatements::default_htmesh_level if they don't exist.
617  *
618  * @return true in case of success, false in case of an error
619  */
620  bool initialize_db();
621 
622  /**
623  * Reads the database version and the htmesh level from the
624  * database. If the meta table does not exist, the default vaulues
625  * SqlStatements::current_db_version and
626  * SqlStatements::default_htmesh_level.
627  *
628  * @return [version, htmesh-level, is-init?]
629  */
630  std::tuple<int, int, bool> get_db_meta();
631 
632  /**
633  * Gets a vector of catalog ids of catalogs. If \p include_disabled is
634  * `true`, disabled catalogs will be included.
635  */
636  std::vector<int> get_catalog_ids(bool include_enabled = false);
637 
638  /**
639  * Prepares performance critical sql queries.
640  *
641  * @return [success, error]
642  */
643  std::pair<bool, QSqlError> prepare_queries();
644 
645  /**
646  * Read a `CatalogObject` from the tip of the \p query.
647  */
648  CatalogObject read_catalogobject(const QSqlQuery &query) const;
649 
650  /**
651  * Read the first `CatalogObject` from the tip of the \p `query`
652  * that hasn't been exec'd yet.
653  */
654  std::pair<bool, CatalogObject> read_first_object(QSqlQuery &query) const;
655 
656  /**
657  * Read all `CatalogObject`s from the \p query.
658  */
659  CatalogObjectList fetch_objects(QSqlQuery &query) const;
660 
661  /**
662  * Internal implementation to forcably remove a catalog (even the
663  * user catalog, use with caution!)
664  */
665  std::pair<bool, QString> remove_catalog_force(const int id);
666 
667  /**
668  *
669  */
670  CatalogObjectVector _get_objects_in_trixel_generic(QSqlQuery &query, const int trixel);
671 
672  /**
673  * A list of database paths. The index gets stored in the
674  * `CatalogObject` and can be used to retrieve the path to the
675  * database.
676  */
677  static QSet<QString> m_db_paths;
678  //@}
679 };
680 
681 /**
682  * Database related error, thrown when database access fails or an
683  * action does not succeed.
684  *
685  * QSqlError is not used here to encapsulate the database further.
686  */
687 class DatabaseError : std::exception
688 {
689  public:
690  enum class ErrorType
691  {
692  OPEN,
693  VERSION,
694  INIT,
695  CREATE_CATALOG,
696  CREATE_MASTER,
697  NOT_FOUND,
698  PREPARE,
699  UNKNOWN
700  };
701 
702  DatabaseError(QString message, ErrorType type = ErrorType::UNKNOWN,
703  const QSqlError &error = QSqlError())
704  : m_message{ std::move(message) }, m_type{ type }, m_error{ error }, m_report{
705  m_message.toStdString() +
706  (error.text().length() > 0 ? "\nSQL ERROR: " + error.text().toStdString() :
707  std::string(""))
708  } {};
709 
710  const char *what() const noexcept override { return m_report.c_str(); }
711  const QString &message() const noexcept { return m_message; }
712  ErrorType type() const noexcept { return m_type; }
713 
714  private:
715  const QString m_message;
716  const ErrorType m_type;
717  const QSqlError m_error;
718  const std::string m_report;
719 };
720 
721 /** \returns the path to the dso database */
722 QString dso_db_path();
723 
724 /** \returns true and a catalog if the catalog metadata (name, author,
725  ...) can be read */
726 std::pair<bool, Catalog> read_catalog_meta_from_file(const QString &path);
727 
728 
729 
730 
731 /**
732  * A concurrent wrapper around \sa CatalogsDB::DBManager
733  *
734  * This wrapper can be instantiated from the main thread. It spawns
735  * its own thread and moves itself to the thread, allowing the main
736  * thread to call DB operations without blocking the user
737  * interface. It provides a generic wrapper interface, \sa
738  * AsyncDBManager::execute(), to call the methods of DBManager.
739  *
740  * Since it is hard to use signal-slot communication with a generic
741  * wrapper like \sa AsyncDBManager::execute(), a wrapper is provided
742  * for the two most likely overloads of \sa
743  * DBManager::find_objects_by_name, which are time-consuming and
744  * frequently used, and these can be directly invoked from
745  * QMetaObject::invokeMethod or an appropriate signal
746  *
747  * The void override of \sa AsyncDBManager::init() does the most
748  * commonly done thing, which is to open the DSO database
749  *
750  */
751 class AsyncDBManager : public QObject {
752  // Note: Follows the active object pattern described here
753  // https://youtu.be/SncJ3D-fO7g?list=PL6CJYn40gN6jgr-Rpl3J4XDQYhmUnxb-g&t=272
754  Q_OBJECT
755 
756 private:
757  std::shared_ptr<DBManager> m_manager;
758  std::queue<std::unique_ptr<CatalogObjectList>> m_result;
759  std::shared_ptr<QThread> m_thread;
760  QMutex m_resultMutex;
761 
762 public:
763  template <typename... Args>
764  using DBManagerMethod = CatalogObjectList (DBManager::*)(Args...);
765 
766  /**
767  * Constructor, does nothing. Call init() to do the actual setup after the QThread stars
768  */
769  template <typename... Args>
770  AsyncDBManager(Args... args)
771  : QObject(nullptr)
772  , m_manager(nullptr)
773  , m_thread(nullptr)
774  {
775  m_thread.reset(new QThread);
776  moveToThread(m_thread.get());
777  connect(m_thread.get(), &QThread::started, [&]() {
778  init(args...);
779  });
780  m_thread->start();
781 
782  /*
783  * This is an attempt to fix a bug introduced in 5a2ba9f8e8b275f44b7593a50ca66f09cb2f985d
784  * where KStars Crashes on MacOS when launched by double clicking (NOT by Terminal or QT Creator)
785  * and then the find dialog is accessed. For some reason, this fixes it?
786  */
787  #ifdef Q_OS_OSX
788  QThread::msleep(100);
789  #endif
790  }
791 
792  ~AsyncDBManager()
793  {
794  QMetaObject::invokeMethod(this, "cleanup");
795  m_thread->wait();
796  }
797 
798  /**
799  * Return a pointer to the DBManager, for non-DB functionalities
800  */
801  inline std::shared_ptr<DBManager> manager() { return m_manager; }
802 
803  /**
804  * Return a pointer to the QThread object
805  */
806  inline std::shared_ptr<QThread> thread() { return m_thread; }
807 
808  /**
809  * Construct the DBManager object
810  *
811  * Should be done in the thread corresponding to this object
812  */
813  template <typename... Args> void init(Args&&... args)
814  {
815  m_manager = std::make_shared<DBManager>(std::forward<Args>(args)...);
816  }
817 
818  /**
819  * A generic wrapper to call any method on DBManager that returns a CatalogObjectList
820  *
821  * For practical use examples, see \sa
822  * AsyncDBManager::find_objects_by_name below which uses this
823  * wrapper
824  */
825  template <typename... Args>
826  void execute(DBManagerMethod<Args...> dbManagerMethod, Args... args)
827  {
828  // c.f. https://stackoverflow.com/questions/25392935/wrap-a-function-pointer-in-c-with-variadic-template
829 
830  // N.B. The perfect forwarding idiom is not used because we
831  // also want to be able to bind to lvalue references, whereas
832  // the types deduced for the arguments has to match the type
833  // deduced to identify the function overload to be used.
834  QMutexLocker _{&m_resultMutex};
835  m_result.emplace(
836  std::make_unique<CatalogObjectList>((m_manager.get()->*dbManagerMethod)(args...))
837  );
838  emit resultReady();
839  }
840 
841  /**
842  * execute(), but specialized to find_objects_by_name
843  *
844  * @fixme Code duplication needed to support older compilers
845  */
846  void _find_objects_by_name(const QString& name, const int limit, const bool exactMatchOnly)
847  {
848  // FIXME: Remove this and use execute() once C++1x compilers
849  // support variadic template type deduction properly
850  QMutexLocker _{&m_resultMutex};
851  m_result.emplace(
852  std::make_unique<CatalogObjectList>((m_manager.get()->find_objects_by_name)(name, limit, exactMatchOnly))
853  );
854  emit resultReady();
855  }
856 
857 signals:
858  void resultReady(void);
859  void threadReady(void);
860 
861 
862 public slots:
863 
864  void init()
865  {
866  m_manager = std::make_shared<DBManager>(dso_db_path());
867  }
868 
869  /**
870  * Calls the given DBManager method, storing the result for later retrieval
871  *
872  * \p dbManagerMethod method to execute (must return a CatalogObjectList)
873  * \p args arguments to supply to the method
874  */
875 
876  void find_objects_by_name(const QString& name, const int limit = -1)
877  {
878  // N.B. One cannot have a function pointer to a function with
879  // default arguments, so all arguments must be supplied here
880 
881  // FIXME: Uncomment to use generic wrapper execute() once
882  // compilers gain proper type-deduction support
883  // execute<const QString&, const int, const bool>(
884  // &DBManager::find_objects_by_name,
885  // name, limit, false);
886 
887  _find_objects_by_name(name, limit, false);
888  }
889 
890  void find_objects_by_name_exact(const QString &name)
891  {
892  // FIXME: Uncomment to use generic wrapper execute() once
893  // compilers gain proper type-deduction support
894  // execute<const QString&, const int, const bool>(
895  // &DBManager::find_objects_by_name,
896  // name, 1, true);
897 
898  _find_objects_by_name(name, 1, true);
899  }
900 
901  /**
902  * Returns the result of the previous call, or a nullptr if none exists
903  */
904  std::unique_ptr<CatalogObjectList> result()
905  {
906  QMutexLocker _{&m_resultMutex};
907  if (m_result.empty())
908  {
909  return std::unique_ptr<CatalogObjectList>();
910  }
911  std::unique_ptr<CatalogObjectList> result = std::move(m_result.front());
912  m_result.pop();
913  return result;
914  }
915 
916 private slots:
917 
918  void cleanup()
919  {
920  Q_ASSERT(m_manager.use_count() == 1);
921  m_manager.reset();
922  m_thread->quit();
923  }
924 
925  void emitReady()
926  {
927  emit threadReady();
928  }
929 
930 };
931 
932 } // namespace CatalogsDB
Q_OBJECTQ_OBJECT
const QString & db_file_name() const
Definition: catalogsdb.h:227
CatalogObjectVector get_objects_in_trixel_null_mag(const int trixel)
Definition: catalogsdb.h:267
void msleep(unsigned long msecs)
QString maintainer
The catalog maintainer.
Definition: catalogsdb.h:101
std::pair< bool, QString > import_catalog(const QString &file_path, const bool overwrite=false)
Loads a dumped catalog from path file_path.
Definition: catalogsdb.cpp:800
std::shared_ptr< QThread > thread()
Return a pointer to the QThread object.
Definition: catalogsdb.h:806
bool compile_master_catalog()
Compiles the master catalog by merging the individual catalogs based on oid and precedence and create...
Definition: catalogsdb.cpp:337
std::pair< bool, QString > add_object(const int catalog_id, const SkyObject::TYPE t, const CachingDms &r, const CachingDms &d, const QString &n, const float m=NaN::f, const QString &lname=QString(), const QString &catalog_identifier=QString(), const float a=0.0, const float b=0.0, const double pa=0.0, const float flux=0)
Add a CatalogObject to a table with catalog_id.
Definition: catalogsdb.cpp:687
void _find_objects_by_name(const QString &name, const int limit, const bool exactMatchOnly)
execute(), but specialized to find_objects_by_name
Definition: catalogsdb.h:846
QString author
The author of the catalog.
Definition: catalogsdb.h:59
std::pair< bool, QString > add_objects(const int catalog_id, const CatalogObjectVector &objects)
Add the objects to a table with catalog_id.
QString license
The catalog license.
Definition: catalogsdb.h:96
a dms subclass that caches its sine and cosine values every time the angle is changed.
Definition: cachingdms.h:18
ColorMap get_catalog_colors()
std::pair< bool, QString > remove_catalog(const int id)
remove a catalog
Definition: catalogsdb.cpp:879
CatalogObjectVector get_objects_in_trixel_no_nulls(const int trixel)
Definition: catalogsdb.h:260
void started()
bool enabled
Wether the catalog is enabled.
Definition: catalogsdb.h:80
int find_suitable_catalog_id()
Finds the smallest free id for a catalog.
Definition: catalogsdb.cpp:953
Manages the catalog database and provides an interface to provide an interface to query and modify th...
Definition: catalogsdb.h:182
const std::pair< bool, Catalog > get_catalog(const int id)
Definition: catalogsdb.cpp:378
CatalogObjectList find_objects_by_wildcard(const QString &wildcard, const int limit=-1)
Find an objects by searching the name four wildcard.
bool update_catalog_views()
Updates the all_catalog_view so that it includes all known catalogs.
Definition: catalogsdb.cpp:242
std::pair< bool, QString > set_catalog_enabled(const int id, const bool enabled)
Enable or disable a catalog.
Definition: catalogsdb.cpp:602
void moveToThread(QThread *targetThread)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
std::pair< bool, QString > insert_catalog_colors(const int id, const CatalogColorMap &colors)
Saves the configures colors of the catalog with id id in colors into the database.
std::pair< bool, QString > remove_object(const int catalog_id, const CatalogObject::oid &id)
Remove the catalog object with the oid from the catalog with the catalog_id.
Definition: catalogsdb.cpp:723
QString color
The catalog color in the form [default color];[scheme file name];[color]....
Definition: catalogsdb.h:91
DBManager(const QString &filename)
Constructs a database manager from the filename which is resolved to a path in the kstars data direct...
Definition: catalogsdb.cpp:81
const std::pair< bool, CatalogStatistics > get_master_statistics()
const std::vector< Catalog > get_catalogs(bool include_disabled=false)
Definition: catalogsdb.cpp:621
std::pair< bool, QString > register_catalog(const int id, const QString &name, const bool mut, const bool enabled, const double precedence, const QString &author=cat_defaults.author, const QString &source=cat_defaults.source, const QString &description=cat_defaults.description, const int version=cat_defaults.version, const QString &color=cat_defaults.color, const QString &license=cat_defaults.license, const QString &maintainer=cat_defaults.maintainer, const QDateTime &timestamp=cat_defaults.timestamp)
Registers a new catalog in the database.
Definition: catalogsdb.cpp:309
CatalogObjectList get_objects(float maglim=default_maglim, int limit=-1)
Get limit objects with magnitude smaller than maglim (smaller = brighter) from the database.
Definition: catalogsdb.cpp:562
CatalogObjectList find_objects_by_name(const QString &name, const int limit=-1, const bool exactMatchOnly=false)
Find an objects by name.
Definition: catalogsdb.cpp:490
QString source
The catalog source.
Definition: catalogsdb.h:64
std::pair< bool, QString > update_catalog_meta(const Catalog &cat)
Update the metatadata catalog.
Definition: catalogsdb.cpp:932
std::string toStdString() const const
CatalogObjectList get_objects_all()
Get all objects from the database.
Definition: catalogsdb.cpp:571
A concurrent wrapper around.
Definition: catalogsdb.h:751
double precedence
The precedence level of a catalog.
Definition: catalogsdb.h:54
AsyncDBManager(Args... args)
Constructor, does nothing.
Definition: catalogsdb.h:770
std::pair< bool, QString > dump_catalog(int catalog_id, QString file_path)
Dumps the catalog with id into the file under the path file_path.
Definition: catalogsdb.cpp:738
Holds statistical information about the objects in a catalog.
Definition: catalogsdb.h:118
bool mut
Wether the catalog is mutable.
Definition: catalogsdb.h:75
CatalogObjectVector get_objects_in_trixel(const int trixel)
Definition: catalogsdb.h:253
int version
The catalog version.
Definition: catalogsdb.h:85
QString name
The catalog mame.
Definition: catalogsdb.h:46
std::pair< bool, CatalogObject > get_object(const CatalogObject::oid &oid)
Get an object by oid.
Definition: catalogsdb.cpp:538
int htmesh_level() const
Definition: catalogsdb.h:358
QDateTime timestamp
Build time of the catalog.
Definition: catalogsdb.h:110
void find_objects_by_name(const QString &name, const int limit=-1)
Calls the given DBManager method, storing the result for later retrieval.
Definition: catalogsdb.h:876
std::unique_ptr< CatalogObjectList > result()
Returns the result of the previous call, or a nullptr if none exists.
Definition: catalogsdb.h:904
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QString description
A (short) description for the catalog.
Definition: catalogsdb.h:70
const std::pair< bool, CatalogStatistics > get_catalog_statistics(const int catalog_id)
bool catalog_exists(const int id)
Definition: catalogsdb.cpp:395
void execute(DBManagerMethod< Args... > dbManagerMethod, Args... args)
A generic wrapper to call any method on DBManager that returns a CatalogObjectList.
Definition: catalogsdb.h:826
CatalogObjectList get_objects_in_catalog(SkyObject::TYPE type, const int catalog_id, float maglim=default_maglim, int limit=-1)
Get limit objects from the catalog with catalog_id of type with magnitude smaller than maglim (smalle...
Definition: catalogsdb.cpp:589
void init(Args &&... args)
Construct the DBManager object.
Definition: catalogsdb.h:813
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
Definition: catalogobject.h:40
A simple struct to hold information about catalogs.
Definition: catalogsdb.h:36
std::tuple< bool, const QString, CatalogObjectList > general_master_query(const QString &where, const QString &order_by="", const int limit=-1)
Find an objects by searching the master catlog with a query like SELECT ...
QString message
std::shared_ptr< DBManager > manager()
Return a pointer to the DBManager, for non-DB functionalities.
Definition: catalogsdb.h:801
std::pair< bool, QString > copy_objects(const int id_1, const int id_2)
Clone objects from the catalog with id_1 to another with id_2.
Definition: catalogsdb.cpp:913
Database related error, thrown when database access fails or an action does not succeed.
Definition: catalogsdb.h:687
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Sep 27 2023 04:02:08 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.