28#include <Recurrence.h>
38#include "gnc-sql-connection.hpp"
39#include "gnc-sql-backend.hpp"
40#include "gnc-sql-object-backend.hpp"
41#include "gnc-sql-column-table-entry.hpp"
42#include "gnc-sql-result.hpp"
51#include "gnc-schedxaction-sql.h"
65static QofLogModule log_module = G_LOG_DOMAIN;
66#define VERSION_TABLE_NAME "versions"
67#define MAX_TABLE_NAME_LEN 50
68#define TABLE_COL_NAME "table_name"
69#define VERSION_COL_NAME "table_version"
71using StrVec = std::vector<std::string>;
73static std::string empty_string{};
74static EntryVec version_table
76 gnc_sql_make_table_entry<CT_STRING>(
77 TABLE_COL_NAME, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL),
78 gnc_sql_make_table_entry<CT_INT>(VERSION_COL_NAME, 0, COL_NNUL)
82 QofBackend {}, m_conn{conn}, m_book{book}, m_loading{false},
83 m_in_query{false}, m_is_pristine_db{false}
89GncSqlBackend::~GncSqlBackend()
97 if (m_conn !=
nullptr && m_conn != conn)
99 finalize_version_info();
104GncSqlBackend::create_statement_from_sql(
const std::string& str)
const noexcept
106 auto stmt = m_conn ? m_conn->create_statement_from_sql(str) :
nullptr;
109 PERR (
"SQL error: %s\n", str.c_str());
118 auto result = m_conn ? m_conn->execute_select_statement(stmt) :
nullptr;
119 if (result ==
nullptr)
121 PERR (
"SQL error: %s\n", stmt->to_sql());
128GncSqlBackend::execute_nonselect_statement(
const GncSqlStatementPtr& stmt)
const noexcept
130 int result = m_conn ? m_conn->execute_nonselect_statement(stmt) : -1;
133 PERR (
"SQL error: %s\n", stmt->to_sql());
140GncSqlBackend::quote_string(
const std::string& str)
const noexcept
142 g_return_val_if_fail (m_conn !=
nullptr, empty_string);
145 return m_conn->quote_string(str);
150 const EntryVec& col_table)
const noexcept
152 g_return_val_if_fail (m_conn !=
nullptr,
false);
156 for (
auto const& table_row : col_table)
158 table_row->add_to_table (info_vec);
160 return m_conn->create_table (table_name, info_vec);
166 const EntryVec& col_table)
noexcept
168 if (create_table (table_name, col_table))
169 return set_table_version (table_name, table_version);
175 const std::string& table_name,
176 const EntryVec& col_table)
const noexcept
178 g_return_val_if_fail (m_conn !=
nullptr,
false);
179 return m_conn->create_index(index_name, table_name, col_table);
184 const EntryVec& col_table)
const noexcept
186 g_return_val_if_fail (m_conn !=
nullptr,
false);
190 for (
auto const& table_row : col_table)
192 table_row->add_to_table (info_vec);
194 return m_conn->add_columns_to_table(table_name, info_vec);
198GncSqlBackend::update_progress(
double pct)
const noexcept
200 if (m_percentage !=
nullptr)
201 (m_percentage) (
nullptr, pct);
205GncSqlBackend::finish_progress() const noexcept
207 if (m_percentage !=
nullptr)
208 (m_percentage) (
nullptr, -1.0);
214 for(
auto entry : m_backend_registry)
216 update_progress(101.0);
217 std::get<1>(entry)->create_tables(
this);
222static const StrVec fixed_load_order
223{ GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, GNC_ID_TRANS };
226static const StrVec business_fixed_load_order =
227{ GNC_ID_BILLTERM, GNC_ID_TAXTABLE, GNC_ID_INVOICE };
230GncSqlBackend::ObjectBackendRegistry::load_remaining(
GncSqlBackend* sql_be)
233 auto num_types = m_registry.size();
234 auto num_done = fixed_load_order.size() + business_fixed_load_order.size();
236 for (
const auto& entry : m_registry)
239 GncSqlObjectBackendPtr obe =
nullptr;
240 std::tie(type, obe) = entry;
245 if (std::find(fixed_load_order.begin(), fixed_load_order.end(),
246 type) != fixed_load_order.end())
continue;
247 if (std::find(business_fixed_load_order.begin(),
248 business_fixed_load_order.end(),
249 type) != business_fixed_load_order.end())
continue;
252 sql_be->update_progress(num_done * 100 / num_types);
253 obe->load_all (sql_be);
260 gpointer pCompiledQuery;
271 gpointer pCompiledQuery;
276scrub_txn_callback (
QofInstance* inst, [[maybe_unused]]
void* data)
278 auto trans = GNC_TRANSACTION(inst);
288 g_return_if_fail (book != NULL);
290 ENTER (
"sql_be=%p, book=%p",
this, book);
294 if (loadType == LOAD_TYPE_INITIAL_LOAD)
296 assert (
m_book ==
nullptr);
299 auto num_types = m_backend_registry.size();
303 for (
const auto& type : fixed_load_order)
306 auto obe = m_backend_registry.get_object_backend(type);
309 update_progress(num_done * 100 / num_types);
313 for (
const auto& type : business_fixed_load_order)
316 auto obe = m_backend_registry.get_object_backend(type);
319 update_progress(num_done * 100 / num_types);
324 root = gnc_book_get_root_account( book );
328 m_backend_registry.load_remaining(
this);
333 else if (loadType == LOAD_TYPE_LOAD_ALL)
336 auto obe = m_backend_registry.get_object_backend (GNC_ID_TRANS);
337 obe->load_all (
this);
341 std::for_each(m_postload_commodities.begin(), m_postload_commodities.end(),
342 [](gnc_commodity* comm) {
343 gnc_commodity_begin_edit(comm);
344 gnc_commodity_commit_edit(comm);
346 m_postload_commodities.clear();
353 qof_collection_foreach(transactions, scrub_txn_callback,
nullptr);
368GncSqlBackend::write_account_tree(
Account* root)
374 g_return_val_if_fail (root !=
nullptr,
false);
376 auto obe = m_backend_registry.get_object_backend(GNC_ID_ACCOUNT);
377 is_ok = obe->commit (
this, QOF_INSTANCE (root));
381 for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
383 is_ok = obe->commit(
this, QOF_INSTANCE (GNC_ACCOUNT (node->data)));
386 g_list_free (descendants);
388 update_progress(101.0);
394GncSqlBackend::write_accounts()
396 update_progress(101.0);
397 auto is_ok = write_account_tree (gnc_book_get_root_account (
m_book));
400 update_progress(101.0);
408write_tx (Transaction* tx, gpointer data)
412 g_return_val_if_fail (tx != NULL, 0);
413 g_return_val_if_fail (data != NULL, 0);
415 s->commit (QOF_INSTANCE (tx));
416 auto splitbe = s->be->get_object_backend(GNC_ID_SPLIT);
418 split_node !=
nullptr && s->is_ok;
419 split_node = g_list_next (split_node))
421 s->is_ok = splitbe->commit(s->be, QOF_INSTANCE(split_node->data));
423 s->be->update_progress (101.0);
424 return (s->is_ok ? 0 : 1);
428GncSqlBackend::write_transactions()
430 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
433 (void)xaccAccountTreeForEachTransaction (
434 gnc_book_get_root_account (
m_book), write_tx, &data);
435 update_progress(101.0);
440GncSqlBackend::write_template_transactions()
442 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
447 (void)xaccAccountTreeForEachTransaction (ra, write_tx, &data);
448 update_progress(101.0);
455GncSqlBackend::write_schedXactions()
457 GList* schedXactions;
461 schedXactions = gnc_book_get_schedxactions (
m_book)->sx_list;
462 auto obe = m_backend_registry.get_object_backend(GNC_ID_SCHEDXACTION);
464 for (; schedXactions != NULL && is_ok; schedXactions = schedXactions->next)
466 tmpSX =
static_cast<decltype (tmpSX)
> (schedXactions->data);
467 is_ok = obe->commit (
this, QOF_INSTANCE (tmpSX));
469 update_progress(101.0);
477 g_return_if_fail (book != NULL);
478 g_return_if_fail (
m_conn !=
nullptr);
482 update_progress(101.0);
496 auto obe = m_backend_registry.get_object_backend(GNC_ID_BOOK);
497 is_ok = obe->commit (
this, QOF_INSTANCE (book));
501 is_ok = write_accounts();
505 is_ok = write_transactions();
509 is_ok = write_template_transactions();
513 is_ok = write_schedXactions();
517 for (
auto entry : m_backend_registry)
518 std::get<1>(entry)->write (
this);
539 LEAVE (
"book=%p", book);
566 m_postload_commodities.push_back(commodity);
569GncSqlObjectBackendPtr
572 return m_backend_registry.get_object_backend(type);
583 gboolean is_destroying;
586 g_return_if_fail (inst != NULL);
587 g_return_if_fail (
m_conn !=
nullptr);
599 qof_instance_mark_clean (inst);
604 if (strcmp (inst->
e_type,
"PriceDB") == 0)
606 qof_instance_mark_clean (inst);
615 is_infant = qof_instance_get_infant (inst);
617 DEBUG (
"%s dirty = %d, do_free = %d, infant = %d\n",
619 is_dirty, is_destroying, is_infant);
621 if (!is_dirty && !is_destroying)
623 LEAVE (
"!dirty OR !destroying");
629 PERR (
"begin_transaction failed\n");
630 LEAVE (
"Rolled back - database transaction begin error");
636 auto obe = m_backend_registry.get_object_backend(std::string{inst->
e_type});
638 is_ok = obe->commit(
this, inst);
641 PERR (
"Unknown object type '%s'\n", inst->
e_type);
646 qof_instance_mark_clean (inst);
647 LEAVE (
"Rolled back - unknown object type");
656 LEAVE (
"Rolled back - database error");
663 qof_instance_mark_clean (inst);
678 g_return_if_fail (
m_conn !=
nullptr);
681 std::string sql {
"SELECT * FROM "};
682 sql += VERSION_TABLE_NAME;
683 auto stmt =
m_conn->create_statement_from_sql(sql);
684 auto result =
m_conn->execute_select_statement (stmt);
685 for (
const auto& row : *result)
687 auto name = row.get_string_at_col (TABLE_COL_NAME);
688 auto version = row.get_int_at_col (VERSION_COL_NAME);
690 m_versions.push_back(std::make_pair(*name,
static_cast<unsigned int>(*version)));
711 bool ok =
create_table (VERSION_TABLE_NAME, version_table);
733 if (m_is_pristine_db)
736 auto version = std::find_if(m_versions.begin(), m_versions.end(),
737 [table_name](
const VersionPair& version) {
738 return version.first == table_name; });
739 if (version != m_versions.end())
740 return version->second;
755 uint_t version)
noexcept
757 g_return_val_if_fail (version > 0,
false);
759 unsigned int cur_version{0};
760 std::stringstream sql;
761 auto ver_entry = std::find_if(m_versions.begin(), m_versions.end(),
762 [table_name](
const VersionPair& ver) {
763 return ver.first == table_name; });
764 if (ver_entry != m_versions.end())
765 cur_version = ver_entry->second;
766 if (cur_version != version)
768 if (cur_version == 0)
770 sql <<
"INSERT INTO " << VERSION_TABLE_NAME <<
" VALUES('" <<
771 table_name <<
"'," << version <<
")";
772 m_versions.push_back(std::make_pair(table_name, version));
776 sql <<
"UPDATE " << VERSION_TABLE_NAME <<
" SET " <<
777 VERSION_COL_NAME <<
"=" << version <<
" WHERE " <<
778 TABLE_COL_NAME <<
"='" << table_name <<
"'";
779 ver_entry->second = version;
781 auto stmt = create_statement_from_sql(sql.str());
782 auto status = execute_nonselect_statement (stmt);
785 PERR (
"SQL error: %s\n", sql.str().c_str());
796 const EntryVec& col_table)
noexcept
798 DEBUG (
"Upgrading %s table\n", table_name.c_str());
800 auto temp_table_name = table_name +
"_new";
801 create_table (temp_table_name, col_table);
802 std::stringstream sql;
803 sql <<
"INSERT INTO " << temp_table_name <<
" SELECT * FROM " << table_name;
804 auto stmt = create_statement_from_sql(sql.str());
805 execute_nonselect_statement(stmt);
808 sql <<
"DROP TABLE " << table_name;
809 stmt = create_statement_from_sql(sql.str());
810 execute_nonselect_statement(stmt);
813 sql <<
"ALTER TABLE " << temp_table_name <<
" RENAME TO " << table_name;
814 stmt = create_statement_from_sql(sql.str());
815 execute_nonselect_statement(stmt);
820 gpointer pObject,
const EntryVec&
table)
824 for (
auto const& table_row :
table)
826 if (!(table_row->is_autoincr()))
828 table_row->add_to_query (obj_name, pObject, vec);
836 const gpointer pObject,
const EntryVec&
table)
const noexcept
838 g_return_val_if_fail (table_name !=
nullptr,
false);
839 g_return_val_if_fail (obj_name !=
nullptr,
false);
840 g_return_val_if_fail (pObject !=
nullptr,
false);
843 auto sql = std::string{
"SELECT "} +
table[0]->name() +
" FROM " + table_name;
844 auto stmt = create_statement_from_sql(sql.c_str());
845 assert (stmt !=
nullptr);
848 PairVec values{get_object_values(obj_name, pObject,
table)};
851 stmt->add_where_cond(obj_name, values);
852 auto result = execute_select_statement (stmt);
853 return (result !=
nullptr && result->size() > 0);
859 const EntryVec&
table)
const noexcept
861 GncSqlStatementPtr stmt;
863 g_return_val_if_fail (table_name !=
nullptr,
false);
864 g_return_val_if_fail (obj_name !=
nullptr,
false);
865 g_return_val_if_fail (pObject !=
nullptr,
false);
870 stmt = build_insert_statement (table_name, obj_name, pObject,
table);
873 stmt = build_update_statement (table_name, obj_name, pObject,
table);
876 stmt = build_delete_statement (table_name, obj_name, pObject,
table);
881 return (execute_nonselect_statement(stmt) != -1);
887 if (comm ==
nullptr)
return false;
889 auto obe = m_backend_registry.get_object_backend(std::string(inst->
e_type));
890 if (obe && !obe->instance_in_db(
this, inst))
891 return obe->commit(
this, inst);
896GncSqlBackend::build_insert_statement (
const char* table_name,
899 const EntryVec&
table)
const noexcept
901 GncSqlStatementPtr stmt;
903 std::ostringstream sql;
905 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
906 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
907 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
908 PairVec values{get_object_values(obj_name, pObject,
table)};
910 sql <<
"INSERT INTO " << table_name <<
"(";
911 for (
auto const& col_value : values)
913 if (col_value != *values.begin())
915 sql << col_value.first;
919 for (
const auto& col_value : values)
921 if (col_value != *values.begin())
923 sql << col_value.second;
927 stmt = create_statement_from_sql(sql.str());
932GncSqlBackend::build_update_statement(
const gchar* table_name,
934 const EntryVec&
table)
const noexcept
936 GncSqlStatementPtr stmt;
937 std::ostringstream sql;
939 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
940 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
941 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
944 PairVec values{get_object_values (obj_name, pObject,
table)};
947 sql <<
"UPDATE " << table_name <<
" SET ";
949 for (
auto const& col_value : values)
951 if (col_value != *values.begin())
953 sql << col_value.first <<
"=" <<
957 stmt = create_statement_from_sql(sql.str());
961 values.erase(values.begin() + 1, values.end());
962 stmt->add_where_cond(obj_name, values);
967GncSqlBackend::build_delete_statement(
const gchar* table_name,
970 const EntryVec&
table)
const noexcept
972 std::ostringstream sql;
974 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
975 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
976 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
978 sql <<
"DELETE FROM " << table_name;
979 auto stmt = create_statement_from_sql (sql.str());
983 table[0]->add_to_query (obj_name, pObject, values);
984 PairVec col_values{values[0]};
985 stmt->add_where_cond (obj_name, col_values);
990GncSqlBackend::ObjectBackendRegistry::ObjectBackendRegistry()
992 register_backend(std::make_shared<GncSqlBookBackend>());
993 register_backend(std::make_shared<GncSqlCommodityBackend>());
994 register_backend(std::make_shared<GncSqlAccountBackend>());
995 register_backend(std::make_shared<GncSqlBudgetBackend>());
996 register_backend(std::make_shared<GncSqlPriceBackend>());
997 register_backend(std::make_shared<GncSqlTransBackend>());
998 register_backend(std::make_shared<GncSqlSplitBackend>());
999 register_backend(std::make_shared<GncSqlSlotsBackend>());
1000 register_backend(std::make_shared<GncSqlRecurrenceBackend>());
1001 register_backend(std::make_shared<GncSqlSchedXactionBackend>());
1002 register_backend(std::make_shared<GncSqlLotsBackend>());
1003 register_backend(std::make_shared<GncSqlBillTermBackend>());
1004 register_backend(std::make_shared<GncSqlCustomerBackend>());
1005 register_backend(std::make_shared<GncSqlEmployeeBackend>());
1006 register_backend(std::make_shared<GncSqlEntryBackend>());
1007 register_backend(std::make_shared<GncSqlInvoiceBackend>());
1008 register_backend(std::make_shared<GncSqlJobBackend>());
1009 register_backend(std::make_shared<GncSqlOrderBackend>());
1010 register_backend(std::make_shared<GncSqlTaxTableBackend>());
1011 register_backend(std::make_shared<GncSqlVendorBackend>());
1015GncSqlBackend::ObjectBackendRegistry::register_backend(OBEEntry&& entry)
noexcept
1017 m_registry.emplace_back(entry);
1021GncSqlBackend::ObjectBackendRegistry::register_backend(GncSqlObjectBackendPtr obe)
noexcept
1023 m_registry.emplace_back(make_tuple(std::string{obe->type()}, obe));
1026GncSqlObjectBackendPtr
1027GncSqlBackend::ObjectBackendRegistry::get_object_backend(
const std::string& type)
const
1029 auto entry = std::find_if(m_registry.begin(), m_registry.end(),
1030 [type](
const OBEEntry& entry){
1031 return type == std::get<0>(entry);
1033 if (entry == m_registry.end())
1036 return std::get<1>(*entry);
Anchor Scheduled Transaction info in a book.
API for the transaction logger.
Main SQL backend structure.
void load(QofBook *, QofBackendLoadType) override
Load the contents of an SQL database into a book.
QofBook * m_book
The primary, main open book.
void commodity_for_postload_processing(gnc_commodity *)
Register a commodity to be committed after loading is complete.
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
GncSqlConnection * m_conn
SQL connection.
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.
VersionVec m_versions
Version number for each table.
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 create_tables() noexcept
Create/update all tables in the database.
bool m_is_pristine_db
Are we saving to a new pristine db?
void commit(QofInstance *) override
Object editing is complete and the object should be saved.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
void rollback(QofInstance *) override
Object editing has been cancelled.
bool m_loading
We are performing an initial load.
void finalize_version_info() noexcept
Finalizes DB table version information.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
void connect(GncSqlConnection *conn) noexcept
Connect the backend to a GncSqlConnection.
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.
void begin(QofInstance *) override
An object is about to be edited.
void sync(QofBook *) override
Save the contents of a book to an SQL database.
bool save_commodity(gnc_commodity *comm) noexcept
Ensure that a commodity referenced in another object is in fact saved in the database.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
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.
GncSqlObjectBackendPtr get_object_backend(const std::string &type) const noexcept
Get the GncSqlObjectBackend for the indicated type.
Encapsulate the connection to the database.
virtual bool does_table_exist(const std::string &) const noexcept=0
Returns true if successful.
virtual bool begin_transaction() noexcept=0
Returns TRUE if successful, false if error.
virtual bool rollback_transaction() noexcept=0
Returns TRUE if successful, FALSE if error.
virtual bool commit_transaction() noexcept=0
Returns TRUE if successful, FALSE if error.
Pure virtual class to iterate over a query result set.
load and save accounts data to SQL
load and save accounts data to SQL
load and save data to SQL
load and save data to SQL
load and save data to SQL
Commodity handling public routines.
load and save customer data to SQL
load and save employee data to SQL
All type declarations for the whole Gnucash engine.
load and save entry data to SQL
load and save invoice data to SQL
load and save job data to SQL
load and save data to SQL
load and save order data to SQL
Generic api to store and retrieve preferences.
load and save data to SQL
a simple price database for gnucash
load and save accounts data to SQL
load and save accounts data to SQL
load and save tax table data to SQL
load and save data to SQL
load and save vendor data to SQL
Business Invoice Interface.
Tax Table programming interface.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
@ ERR_BACKEND_SERVER_ERR
error in response from server
@ ERR_BACKEND_READONLY
cannot write to file/directory
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database).
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
const gchar * QofIdType
QofIdType declaration.
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
QofIdType e_type
Entity type.
#define DEBUG(format, args...)
Print a debugging message.
#define LEAVE(format, args...)
Print a function exit debugging message.
#define PERR(format, args...)
Log a serious error.
#define ENTER(format, args...)
Print a function entry debugging message.
void set_error(QofBackendError err)
Set the error value only if there isn't already an error already.
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
void xaccLogEnable(void)
document me
void xaccLogDisable(void)
document me
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().