GnuCash c935c2f+
Loading...
Searching...
No Matches
Public Member Functions
GncDbiBackend< Type > Class Template Reference
Inheritance diagram for GncDbiBackend< Type >:
GncSqlBackend QofBackend

Public Member Functions

 GncDbiBackend (GncSqlConnection *conn, QofBook *book)
 
void session_begin (QofSession *, const char *, SessionOpenMode) override
 Open the file or connect to the server.
 
void session_end () override
 
void load (QofBook *, QofBackendLoadType) override
 Load the minimal set of application data needed for the application to be operable at initial startup.
 
void safe_sync (QofBook *) override
 Safely resave a database by renaming all of its tables, recreating everything, and then dropping the backup tables only if there were no errors.
 
bool connected () const noexcept
 
void set_dbi_error (QofBackendError error, unsigned int repeat, bool retry) noexcept
 FIXME: Just a pass-through to m_conn:
 
void retry_connection (const char *msg) const noexcept
 
bool exists ()
 
void set_exists (bool exists)
 
void session_begin (QofSession *session, const char *new_uri, SessionOpenMode mode)
 Open the file or connect to the server.
 
void safe_sync (QofBook *book)
 Perform a sync in a way that prevents data loss on a DBI backend.
 
- Public Member Functions inherited from GncSqlBackend
 GncSqlBackend (GncSqlConnection *conn, QofBook *book)
 
void load (QofBook *, QofBackendLoadType) override
 Load the contents of an SQL database into a book.
 
void sync (QofBook *) override
 Save the contents of a book to an SQL database.
 
void begin (QofInstance *) override
 An object is about to be edited.
 
void commit (QofInstance *) override
 Object editing is complete and the object should be saved.
 
void rollback (QofInstance *) override
 Object editing has been cancelled.
 
void connect (GncSqlConnection *conn) noexcept
 Connect the backend to a GncSqlConnection.
 
void init_version_info () noexcept
 Initializes DB table version information.
 
bool reset_version_info () noexcept
 Resets the version table information by removing all version table info.
 
void finalize_version_info () noexcept
 Finalizes DB table version information.
 
GncSqlStatementPtr create_statement_from_sql (const std::string &str) const noexcept
 
GncSqlResultPtr execute_select_statement (const GncSqlStatementPtr &stmt) const noexcept
 Executes an SQL SELECT statement and returns the result rows.
 
int execute_nonselect_statement (const GncSqlStatementPtr &stmt) const noexcept
 
std::string quote_string (const std::string &) const noexcept
 
bool create_table (const std::string &table_name, const EntryVec &col_table) const noexcept
 Creates a table in the database.
 
bool create_table (const std::string &table_name, int table_version, const EntryVec &col_table) noexcept
 Creates a table in the database and sets its version.
 
void create_tables () noexcept
 Create/update all tables in the database.
 
bool create_index (const std::string &index_name, const std::string &table_name, const EntryVec &col_table) const noexcept
 Creates an index in the database.
 
bool add_columns_to_table (const std::string &table_name, const EntryVec &col_table) const noexcept
 Adds one or more columns to an existing table.
 
void upgrade_table (const std::string &table_name, const EntryVec &col_table) noexcept
 Upgrades a table to a new structure.
 
uint_t get_table_version (const std::string &table_name) const noexcept
 Returns the version number for a DB table.
 
bool set_table_version (const std::string &table_name, uint_t version) noexcept
 Registers the version for a table.
 
void commodity_for_postload_processing (gnc_commodity *)
 Register a commodity to be committed after loading is complete.
 
GncSqlObjectBackendPtr get_object_backend (const std::string &type) const noexcept
 Get the GncSqlObjectBackend for the indicated type.
 
bool object_in_db (const char *table_name, QofIdTypeConst obj_name, const gpointer pObject, const EntryVec &table) const noexcept
 Checks whether an object is in the database or not.
 
bool do_db_operation (E_DB_OPERATION op, const char *table_name, QofIdTypeConst obj_name, gpointer pObject, const EntryVec &table) const noexcept
 Performs an operation on the database.
 
bool save_commodity (gnc_commodity *comm) noexcept
 Ensure that a commodity referenced in another object is in fact saved in the database.
 
QofBookbook () const noexcept
 
void set_loading (bool loading) noexcept
 
bool pristine () const noexcept
 
void update_progress (double pct) const noexcept
 
void finish_progress () const noexcept
 
- Public Member Functions inherited from QofBackend
 QofBackend (const QofBackend &)=delete
 
 QofBackend (const QofBackend &&)=delete
 
virtual void export_coa (QofBook *)
 Extract the chart of accounts from the current database and create a new database with it.
 
void set_error (QofBackendError err)
 Set the error value only if there isn't already an error already.
 
QofBackendError get_error ()
 Retrieve the currently-stored error and clear it.
 
bool check_error ()
 Report if there is an error.
 
void set_message (std::string &&)
 Set a descriptive message that can be displayed to the user when there's an error.
 
const std::string && get_message ()
 Retrieve and clear the stored error message.
 
void set_percentage (QofBePercentageFunc pctfn)
 Store and retrieve a backend-specific function for determining the progress in completing a long operation, for use with a progress meter.
 
QofBePercentageFunc get_percentage ()
 
const std::string & get_uri ()
 Retrieve the backend's storage URI.
 

Additional Inherited Members

- Static Public Member Functions inherited from QofBackend
static bool register_backend (const char *, const char *)
 Class methods for dynamically loading the several backends and for freeing them at shutdown.
 
static void release_backends ()
 
- Protected Attributes inherited from GncSqlBackend
GncSqlConnectionm_conn = nullptr
 SQL connection.
 
QofBookm_book = nullptr
 The primary, main open book.
 
bool m_loading
 We are performing an initial load.
 
bool m_in_query
 We are processing a query.
 
bool m_is_pristine_db
 Are we saving to a new pristine db?
 
const char * m_time_format = nullptr
 Server-specific date-time string format.
 
VersionVec m_versions
 Version number for each table.
 
- Protected Attributes inherited from QofBackend
QofBePercentageFunc m_percentage
 
std::string m_fullpath
 Each backend resolves a fully-qualified file path.
 

Detailed Description

template<DbType Type>
class GncDbiBackend< Type >

Definition at line 88 of file gnc-backend-dbi.hpp.

Constructor & Destructor Documentation

◆ GncDbiBackend()

template<DbType Type>
GncDbiBackend< Type >::GncDbiBackend ( GncSqlConnection conn,
QofBook book 
)
inline

Definition at line 91 of file gnc-backend-dbi.hpp.

91 :
92 GncSqlBackend(conn, book), m_exists{false} {}
Main SQL backend structure.

◆ ~GncDbiBackend()

template<DbType Type>
GncDbiBackend< Type >::~GncDbiBackend ( )

Definition at line 844 of file gnc-backend-dbi.cpp.

845{
846 /* Stop transaction logging */
847 xaccLogSetBaseName (nullptr);
848}
void xaccLogSetBaseName(const char *basepath)
The xaccLogSetBaseName() method sets the base filepath and the root part of the journal file name.
Definition TransLog.cpp:120

Member Function Documentation

◆ connected()

template<DbType Type>
bool GncDbiBackend< Type >::connected ( ) const
inlinenoexcept

Definition at line 98 of file gnc-backend-dbi.hpp.

98{ return m_conn != nullptr; }
GncSqlConnection * m_conn
SQL connection.

◆ exists()

template<DbType Type>
bool GncDbiBackend< Type >::exists ( )
inline

Definition at line 110 of file gnc-backend-dbi.hpp.

110{ return m_exists; }

◆ load()

template<DbType Type>
void GncDbiBackend< Type >::load ( QofBook ,
QofBackendLoadType   
)
overridevirtual

Load the minimal set of application data needed for the application to be operable at initial startup.

It is assumed that the application will perform a 'run_query()' to obtain any additional data that it needs. For file-based backends, it is acceptable for the backend to return all data at load time; for SQL-based backends, it is acceptable for the backend to return no data.

Thus, for example, the old GnuCash postgres backend returned the account tree, all currencies, and the pricedb, as these were needed at startup. It did not have to return any transactions whatsoever, as these were obtained at a later stage when a user opened a register, resulting in a query being sent to the backend. The current DBI backend on the other hand loads the entire database into memory.

(Its OK to send over entities at this point, but one should be careful of the network load; also, its possible that whatever is sent is not what the user wanted anyway, which is why its better to wait for the query).

Implements QofBackend.

Definition at line 862 of file gnc-backend-dbi.cpp.

863{
864 g_return_if_fail (book != nullptr);
865
866 ENTER ("dbi_be=%p, book=%p", this, book);
867
868 if (loadType == LOAD_TYPE_INITIAL_LOAD)
869 {
870
871 // Set up table version information
873 assert (m_book == nullptr);
875 }
876
877 GncSqlBackend::load(book, loadType);
878
879 if (Type == DbType::DBI_SQLITE)
880 gnc_features_set_used(book, GNC_FEATURE_SQLITE3_ISO_DATES);
881
882 if (GNUCASH_RESAVE_VERSION > get_table_version("Gnucash"))
883 {
884 /* The database was loaded with an older database schema or
885 * data semantics. In order to ensure consistency, the whole
886 * thing needs to be saved anew. */
888 }
889 else if (GNUCASH_RESAVE_VERSION < get_table_version("Gnucash-Resave"))
890 {
891 /* Worse, the database was created with a newer version. We
892 * can't safely write to this database, so the user will have
893 * to do a "save as" to make one that we can write to.
894 */
896 }
897
898
899 LEAVE ("");
900}
void load(QofBook *, QofBackendLoadType) override
Load the contents of an SQL database into a book.
QofBook * m_book
The primary, main open book.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
void create_tables() noexcept
Create/update all tables in the database.
void init_version_info() noexcept
Initializes DB table version information.
@ ERR_SQL_DB_TOO_OLD
database is old and needs upgrading
Definition qofbackend.h:113
@ ERR_SQL_DB_TOO_NEW
database is newer, we can't write to it
Definition qofbackend.h:114
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
void set_error(QofBackendError err)
Set the error value only if there isn't already an error already.
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.

◆ retry_connection()

template<DbType Type>
void GncDbiBackend< Type >::retry_connection ( const char *  msg) const
inlinenoexcept

Definition at line 105 of file gnc-backend-dbi.hpp.

106 {
107 m_conn->retry_connection(msg);
108 }

◆ safe_sync() [1/2]

template<DbType Type>
void GncDbiBackend< Type >::safe_sync ( QofBook book)
overridevirtual

Safely resave a database by renaming all of its tables, recreating everything, and then dropping the backup tables only if there were no errors.

If there are errors, drop the new tables and restore the originals.

Parameters
bookQofBook to be saved in the database.

Implements QofBackend.

Definition at line 946 of file gnc-backend-dbi.cpp.

947{
948 auto conn = dynamic_cast<GncDbiSqlConnection*>(m_conn);
949
950 g_return_if_fail (conn != nullptr);
951 g_return_if_fail (book != nullptr);
952
953 ENTER ("book=%p, primary=%p", book, m_book);
954 if (!conn->begin_transaction())
955 {
956 LEAVE("Failed to obtain a transaction.");
957 return;
958 }
959 if (!conn->table_operation (TableOpType::backup))
960 {
961 conn->rollback_transaction();
962 LEAVE ("Failed to rename tables");
963 return;
964 }
965 if (!conn->drop_indexes())
966 {
967 conn->rollback_transaction();
968 LEAVE ("Failed to drop indexes");
969 return;
970 }
971
972 sync(m_book);
973 if (check_error())
974 {
975 conn->rollback_transaction();
976 LEAVE ("Failed to create new database tables");
977 return;
978 }
979 conn->commit_transaction();
980 conn->table_operation (TableOpType::drop_backup);
981 LEAVE ("book=%p", m_book);
982}
Encapsulate a libdbi dbi_conn connection.
void sync(QofBook *) override
Save the contents of a book to an SQL database.
bool check_error()
Report if there is an error.

◆ safe_sync() [2/2]

void GncDbiBackend< DbType::DBI_MYSQL >::safe_sync ( QofBook )
virtual

Perform a sync in a way that prevents data loss on a DBI backend.

Implements QofBackend.

Definition at line 989 of file gnc-backend-dbi.cpp.

990{
991 auto conn = dynamic_cast<GncDbiSqlConnection*>(m_conn);
992
993 g_return_if_fail (conn != nullptr);
994 g_return_if_fail (book != nullptr);
995
996 ENTER ("book=%p, primary=%p", book, m_book);
997 if (!conn->table_operation (TableOpType::backup))
998 {
1000 conn->table_operation (TableOpType::rollback);
1001 LEAVE ("Failed to rename tables");
1002 return;
1003 }
1004 if (!conn->drop_indexes())
1005 {
1006 conn->table_operation (TableOpType::rollback);
1008 set_message("Failed to drop indexes");
1009 LEAVE ("Failed to drop indexes");
1010 return;
1011 }
1012
1013 sync(m_book);
1014 if (check_error())
1015 {
1016 conn->table_operation (TableOpType::rollback);
1017 LEAVE ("Failed to create new database tables");
1018 return;
1019 }
1020 conn->table_operation (TableOpType::drop_backup);
1021 LEAVE ("book=%p", m_book);
1022}
@ ERR_BACKEND_SERVER_ERR
error in response from server
Definition qofbackend.h:71
void set_message(std::string &&)
Set a descriptive message that can be displayed to the user when there's an error.

◆ session_begin() [1/2]

template<DbType Type>
void GncDbiBackend< Type >::session_begin ( QofSession *  session,
const char *  new_uri,
SessionOpenMode  mode 
)
overridevirtual

Open the file or connect to the server.

Parameters
sessionThe QofSession that will control the backend.
new_uriThe location of the data store that the backend will use.
modeThe session open mode. See qof_session_begin().

Implements QofBackend.

Definition at line 637 of file gnc-backend-dbi.cpp.

639{
640 PairVec options;
641
642 g_return_if_fail (session != nullptr);
643 g_return_if_fail (new_uri != nullptr);
644
645 ENTER (" ");
646
647 /* Split the book-id
648 * Format is protocol://username:password@hostname:port/dbname
649 where username, password and port are optional) */
650 UriStrings uri(new_uri);
651
652 if (Type == DbType::DBI_PGSQL)
653 {
654 if (uri.m_portnum == 0)
655 uri.m_portnum = PGSQL_DEFAULT_PORT;
656 /* Postgres's SQL interface coerces identifiers to lower case, but the
657 * C interface is case-sensitive. This results in a mixed-case dbname
658 * being created (with a lower case name) but then dbi can't connect to
659 * it. To work around this, coerce the name to lowercase first. */
660 auto lcname = g_utf8_strdown (uri.dbname(), -1);
661 uri.m_dbname = std::string{lcname};
662 g_free(lcname);
663 }
664 connect(nullptr);
665
666 bool create{mode == SESSION_NEW_STORE || mode == SESSION_NEW_OVERWRITE};
667 auto conn = conn_setup(options, uri);
668 if (conn == nullptr)
669 {
670 LEAVE("Error");
671 return;
672 }
673
674 m_exists = true; //May be unset in the error handler.
675 auto result = dbi_conn_connect (conn);
676 if (result == 0)
677 {
678 if (Type == DbType::DBI_MYSQL)
679 adjust_sql_options (conn);
680 if (mode != SESSION_READ_ONLY && !conn_test_dbi_library(conn))
681 {
682 dbi_conn_close(conn);
683 LEAVE("Error");
684 return;
685 }
686 bool create = (mode == SESSION_NEW_STORE ||
687 mode == SESSION_NEW_OVERWRITE);
688 if (create && save_may_clobber_data<Type>(conn, uri.quote_dbname(Type)))
689 {
690 if (mode == SESSION_NEW_OVERWRITE)
691 {
692 if (!drop_database<Type>(conn, uri))
693 return;
694 }
695 else
696 {
698 PWARN ("Database already exists, Might clobber it.");
699 dbi_conn_close(conn);
700 LEAVE("Error");
701 return;
702 }
703 /* Drop successful. */
704 m_exists = false;
705 }
706
707 }
708 else if (m_exists)
709 {
710 PERR ("Unable to connect to database '%s'\n", uri.dbname());
712 dbi_conn_close(conn);
713 LEAVE("Error");
714 return;
715 }
716 else if (!create)
717 {
718 PERR ("Database '%s' does not exist\n", uri.dbname());
720 std::string msg{"Database "};
721 set_message(msg + uri.dbname() + " not found");
722 LEAVE("Error");
723 return;
724 }
725
726 if (create)
727 {
728 if (!m_exists &&
729 !create_database(conn, uri.quote_dbname(Type).c_str()))
730 {
731 dbi_conn_close(conn);
732 LEAVE("Error");
733 return;
734 }
735 conn = conn_setup(options, uri);
736 result = dbi_conn_connect (conn);
737 if (result < 0)
738 {
739 PERR ("Unable to create database '%s'\n", uri.dbname());
741 dbi_conn_close(conn);
742 LEAVE("Error");
743 return;
744 }
745 if (Type == DbType::DBI_MYSQL)
746 adjust_sql_options (conn);
747 if (!conn_test_dbi_library(conn))
748 {
749 if (Type == DbType::DBI_PGSQL)
750 dbi_conn_select_db (conn, "template1");
751 dbi_conn_queryf (conn, "DROP DATABASE %s",
752 uri.quote_dbname(Type).c_str());
753 dbi_conn_close(conn);
754 return;
755 }
756 }
757
758 connect(nullptr);
759 try
760 {
761 connect(new GncDbiSqlConnection(Type, this, conn, mode));
762 }
763 catch (std::runtime_error& err)
764 {
765 return;
766 }
767 /* We should now have a proper session set up.
768 * Let's start logging */
769 auto translog_path = gnc_build_translog_path (uri.basename().c_str());
770 xaccLogSetBaseName (translog_path);
771 PINFO ("logpath=%s", translog_path ? translog_path : "(null)");
772 g_free (translog_path);
773
774 LEAVE (" ");
775}
void connect(GncSqlConnection *conn) noexcept
Connect the backend to a GncSqlConnection.
@ ERR_BACKEND_NO_SUCH_DB
the named database doesn't exist
Definition qofbackend.h:63
@ ERR_BACKEND_STORE_EXISTS
File exists, data would be destroyed.
Definition qofbackend.h:67
@ ERR_BACKEND_CANT_CONNECT
bad dbname/login/passwd or network failure
Definition qofbackend.h:64
@ SESSION_READ_ONLY
Create a new store at the URI even if a store already exists there.
Definition qofsession.h:128
@ SESSION_NEW_STORE
Open will fail if the URI doesn't exist or is locked.
Definition qofsession.h:124
@ SESSION_NEW_OVERWRITE
Create a new store at the URI.
Definition qofsession.h:126
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250

◆ session_begin() [2/2]

void GncDbiBackend< DbType::DBI_SQLITE >::session_begin ( QofSession *  session,
const char *  new_uri,
SessionOpenMode  mode 
)
virtual

Open the file or connect to the server.

Parameters
sessionThe QofSession that will control the backend.
new_uriThe location of the data store that the backend will use.
modeThe session open mode. See qof_session_begin().

Implements QofBackend.

Definition at line 356 of file gnc-backend-dbi.cpp.

359{
360 gboolean file_exists;
361 PairVec options;
362
363 g_return_if_fail (session != nullptr);
364 g_return_if_fail (new_uri != nullptr);
365
366 ENTER (" ");
367
368 /* Remove uri type if present */
369 auto path = gnc_uri_get_path (new_uri);
370 std::string filepath{path};
371 g_free(path);
372 GFileTest ftest = static_cast<decltype (ftest)> (
373 G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS) ;
374 file_exists = g_file_test (filepath.c_str(), ftest);
375 bool create{mode == SESSION_NEW_STORE || mode == SESSION_NEW_OVERWRITE};
376 if (!create && !file_exists)
377 {
378 set_error (ERR_FILEIO_FILE_NOT_FOUND);
379 std::string msg{"Sqlite3 file "};
380 set_message (msg + filepath + " not found");
381 PWARN ("Sqlite3 file %s not found", filepath.c_str());
382 LEAVE("Error");
383 return;
384 }
385
386 if (create && file_exists)
387 {
388 if (mode == SESSION_NEW_OVERWRITE)
389 g_unlink (filepath.c_str());
390 else
391 {
393 auto msg = "Might clobber, mode not SESSION_NEW_OVERWRITE";
394 PWARN ("%s", msg);
395 LEAVE("Error");
396 return;
397 }
398 }
399
400 connect(nullptr);
401 /* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
402 options.push_back(std::make_pair("host", "localhost"));
403 auto dirname = g_path_get_dirname (filepath.c_str());
404 auto basename = g_path_get_basename (filepath.c_str());
405 options.push_back(std::make_pair("dbname", basename));
406 options.push_back(std::make_pair("sqlite3_dbdir", dirname));
407 if (basename != nullptr) g_free (basename);
408 if (dirname != nullptr) g_free (dirname);
409 UriStrings uri;
410 auto conn = conn_setup(options, uri);
411 if (conn == nullptr)
412 {
413 LEAVE("Error");
414 return;
415 }
416
417 auto result = dbi_conn_connect (conn);
418
419 if (result < 0)
420 {
421 dbi_conn_close(conn);
422 PERR ("Unable to connect to %s: %d\n", new_uri, result);
424 LEAVE("Error");
425 return;
426 }
427
428 if (mode != SESSION_READ_ONLY && !conn_test_dbi_library(conn))
429 {
430 if (create && !file_exists)
431 {
432 /* File didn't exist before, but it does now, and we don't want to
433 * leave it lying around.
434 */
435 dbi_conn_close (conn);
436 conn = nullptr;
437 g_unlink (filepath.c_str());
438 }
439 dbi_conn_close(conn);
440 LEAVE("Bad DBI Library");
441 return;
442 }
443
444 try
445 {
446 connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
447 this, conn, mode));
448 }
449 catch (std::runtime_error& err)
450 {
451 return;
452 }
453
454 /* We should now have a proper session set up.
455 * Let's start logging */
456 xaccLogSetBaseName (filepath.c_str());
457 PINFO ("logpath=%s", filepath.c_str() ? filepath.c_str() : "(null)");
458 LEAVE ("");
459}
@ ERR_BACKEND_BAD_URL
Can't parse url.
Definition qofbackend.h:62
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.

◆ session_end()

template<DbType Type>
void GncDbiBackend< Type >::session_end ( )
overridevirtual

Implements QofBackend.

Definition at line 833 of file gnc-backend-dbi.cpp.

834{
835 ENTER (" ");
836
838 connect(nullptr);
839
840 LEAVE (" ");
841}
void finalize_version_info() noexcept
Finalizes DB table version information.

◆ set_dbi_error()

template<DbType Type>
void GncDbiBackend< Type >::set_dbi_error ( QofBackendError  error,
unsigned int  repeat,
bool  retry 
)
inlinenoexcept

FIXME: Just a pass-through to m_conn:

Definition at line 100 of file gnc-backend-dbi.hpp.

102 {
103 m_conn->set_error(error, repeat, retry);
104 }

◆ set_exists()

template<DbType Type>
void GncDbiBackend< Type >::set_exists ( bool  exists)
inline

Definition at line 111 of file gnc-backend-dbi.hpp.

111{ m_exists = exists; }

The documentation for this class was generated from the following files: