28 #include <Recurrence.h> 37 #include "gnc-sql-connection.hpp" 38 #include "gnc-sql-backend.hpp" 39 #include "gnc-sql-object-backend.hpp" 40 #include "gnc-sql-column-table-entry.hpp" 41 #include "gnc-sql-result.hpp" 50 #include "gnc-schedxaction-sql.h" 65 #define VERSION_TABLE_NAME "versions" 66 #define MAX_TABLE_NAME_LEN 50 67 #define TABLE_COL_NAME "table_name" 68 #define VERSION_COL_NAME "table_version" 70 using StrVec = std::vector<std::string>;
72 static std::string empty_string{};
73 static EntryVec version_table
75 gnc_sql_make_table_entry<CT_STRING>(
76 TABLE_COL_NAME, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL),
77 gnc_sql_make_table_entry<CT_INT>(VERSION_COL_NAME, 0, COL_NNUL)
81 QofBackend {}, m_conn{conn}, m_book{book}, m_loading{
false},
82 m_in_query{
false}, m_is_pristine_db{
false}
88 GncSqlBackend::~GncSqlBackend()
96 if (m_conn !=
nullptr && m_conn != conn)
98 finalize_version_info();
103 GncSqlBackend::create_statement_from_sql(
const std::string& str)
const noexcept
105 auto stmt = m_conn ? m_conn->create_statement_from_sql(str) :
nullptr;
108 PERR (
"SQL error: %s\n", str.c_str());
117 auto result = m_conn ? m_conn->execute_select_statement(stmt) :
nullptr;
118 if (result ==
nullptr)
120 PERR (
"SQL error: %s\n", stmt->to_sql());
127 GncSqlBackend::execute_nonselect_statement(
const GncSqlStatementPtr& stmt)
const noexcept
129 int result = m_conn ? m_conn->execute_nonselect_statement(stmt) : -1;
132 PERR (
"SQL error: %s\n", stmt->to_sql());
139 GncSqlBackend::quote_string(
const std::string& str)
const noexcept
141 g_return_val_if_fail (m_conn !=
nullptr, empty_string);
144 return m_conn->quote_string(str);
149 const EntryVec& col_table)
const noexcept
151 g_return_val_if_fail (m_conn !=
nullptr,
false);
155 for (
auto const& table_row : col_table)
157 table_row->add_to_table (info_vec);
159 return m_conn->create_table (table_name, info_vec);
165 const EntryVec& col_table) noexcept
167 if (create_table (table_name, col_table))
168 return set_table_version (table_name, table_version);
174 const std::string& table_name,
175 const EntryVec& col_table)
const noexcept
177 g_return_val_if_fail (m_conn !=
nullptr,
false);
178 return m_conn->create_index(index_name, table_name, col_table);
183 const EntryVec& col_table)
const noexcept
185 g_return_val_if_fail (m_conn !=
nullptr,
false);
189 for (
auto const& table_row : col_table)
191 table_row->add_to_table (info_vec);
193 return m_conn->add_columns_to_table(table_name, info_vec);
197 GncSqlBackend::update_progress(
double pct)
const noexcept
199 if (m_percentage !=
nullptr)
200 (m_percentage) (
nullptr, pct);
204 GncSqlBackend::finish_progress() const noexcept
206 if (m_percentage !=
nullptr)
207 (m_percentage) (
nullptr, -1.0);
213 for(
auto entry : m_backend_registry)
215 update_progress(101.0);
221 static const StrVec fixed_load_order
222 { GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, GNC_ID_TRANS };
225 static const StrVec business_fixed_load_order =
226 { GNC_ID_BILLTERM, GNC_ID_TAXTABLE, GNC_ID_INVOICE };
229 GncSqlBackend::ObjectBackendRegistry::load_remaining(
GncSqlBackend* sql_be)
232 auto num_types = m_registry.size();
233 auto num_done = fixed_load_order.size() + business_fixed_load_order.size();
235 for (
const auto& entry : m_registry)
238 GncSqlObjectBackendPtr obe =
nullptr;
239 std::tie(type, obe) = entry;
244 if (std::find(fixed_load_order.begin(), fixed_load_order.end(),
245 type) != fixed_load_order.end())
continue;
246 if (std::find(business_fixed_load_order.begin(),
247 business_fixed_load_order.end(),
248 type) != business_fixed_load_order.end())
continue;
251 sql_be->update_progress(num_done * 100 / num_types);
252 obe->load_all (sql_be);
259 gpointer pCompiledQuery;
270 gpointer pCompiledQuery;
275 scrub_txn_callback (
QofInstance* inst, [[maybe_unused]]
void* data)
277 auto trans = GNC_TRANSACTION(inst);
287 g_return_if_fail (book != NULL);
289 ENTER (
"sql_be=%p, book=%p",
this, book);
293 if (loadType == LOAD_TYPE_INITIAL_LOAD)
295 assert (
m_book ==
nullptr);
298 auto num_types = m_backend_registry.size();
302 for (
const auto& type : fixed_load_order)
305 auto obe = m_backend_registry.get_object_backend(type);
308 update_progress(num_done * 100 / num_types);
312 for (
const auto& type : business_fixed_load_order)
315 auto obe = m_backend_registry.get_object_backend(type);
318 update_progress(num_done * 100 / num_types);
323 root = gnc_book_get_root_account( book );
327 m_backend_registry.load_remaining(
this);
332 else if (loadType == LOAD_TYPE_LOAD_ALL)
335 auto obe = m_backend_registry.get_object_backend (GNC_ID_TRANS);
336 obe->load_all (
this);
340 std::for_each(m_postload_commodities.begin(), m_postload_commodities.end(),
341 [](gnc_commodity* comm) {
342 gnc_commodity_begin_edit(comm);
343 gnc_commodity_commit_edit(comm);
345 m_postload_commodities.clear();
351 qof_collection_foreach(transactions, scrub_txn_callback,
nullptr);
365 GncSqlBackend::write_account_tree(
Account* root)
371 g_return_val_if_fail (root !=
nullptr,
false);
373 auto obe = m_backend_registry.get_object_backend(GNC_ID_ACCOUNT);
374 is_ok = obe->commit (
this, QOF_INSTANCE (root));
378 for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
380 is_ok = obe->commit(
this, QOF_INSTANCE (GNC_ACCOUNT (node->data)));
383 g_list_free (descendants);
385 update_progress(101.0);
391 GncSqlBackend::write_accounts()
393 update_progress(101.0);
394 auto is_ok = write_account_tree (gnc_book_get_root_account (
m_book));
397 update_progress(101.0);
405 write_tx (Transaction* tx, gpointer data)
409 g_return_val_if_fail (tx != NULL, 0);
410 g_return_val_if_fail (data != NULL, 0);
412 s->commit (QOF_INSTANCE (tx));
413 auto splitbe = s->be->get_object_backend(GNC_ID_SPLIT);
415 split_node !=
nullptr && s->is_ok;
416 split_node = g_list_next (split_node))
418 s->is_ok = splitbe->commit(s->be, QOF_INSTANCE(split_node->data));
420 s->be->update_progress (101.0);
421 return (s->is_ok ? 0 : 1);
425 GncSqlBackend::write_transactions()
427 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
431 gnc_book_get_root_account (
m_book), write_tx, &data);
432 update_progress(101.0);
437 GncSqlBackend::write_template_transactions()
439 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
445 update_progress(101.0);
452 GncSqlBackend::write_schedXactions()
454 GList* schedXactions;
458 schedXactions = gnc_book_get_schedxactions (
m_book)->sx_list;
459 auto obe = m_backend_registry.get_object_backend(GNC_ID_SCHEDXACTION);
461 for (; schedXactions != NULL && is_ok; schedXactions = schedXactions->next)
463 tmpSX =
static_cast<decltype (tmpSX)
> (schedXactions->data);
464 is_ok = obe->commit (
this, QOF_INSTANCE (tmpSX));
466 update_progress(101.0);
474 g_return_if_fail (book != NULL);
475 g_return_if_fail (
m_conn !=
nullptr);
479 update_progress(101.0);
493 auto obe = m_backend_registry.get_object_backend(GNC_ID_BOOK);
494 is_ok = obe->commit (
this, QOF_INSTANCE (book));
498 is_ok = write_accounts();
502 is_ok = write_transactions();
506 is_ok = write_template_transactions();
510 is_ok = write_schedXactions();
514 for (
auto entry : m_backend_registry)
515 std::get<1>(entry)->write (
this);
536 LEAVE (
"book=%p", book);
563 m_postload_commodities.push_back(commodity);
566 GncSqlObjectBackendPtr
569 return m_backend_registry.get_object_backend(type);
580 gboolean is_destroying;
583 g_return_if_fail (inst != NULL);
584 g_return_if_fail (
m_conn !=
nullptr);
596 qof_instance_mark_clean (inst);
601 if (strcmp (inst->
e_type,
"PriceDB") == 0)
603 qof_instance_mark_clean (inst);
612 is_infant = qof_instance_get_infant (inst);
614 DEBUG (
"%s dirty = %d, do_free = %d, infant = %d\n",
616 is_dirty, is_destroying, is_infant);
618 if (!is_dirty && !is_destroying)
620 LEAVE (
"!dirty OR !destroying");
626 PERR (
"begin_transaction failed\n");
627 LEAVE (
"Rolled back - database transaction begin error");
633 auto obe = m_backend_registry.get_object_backend(std::string{inst->
e_type});
635 is_ok = obe->commit(
this, inst);
638 PERR (
"Unknown object type '%s'\n", inst->
e_type);
643 qof_instance_mark_clean (inst);
644 LEAVE (
"Rolled back - unknown object type");
653 LEAVE (
"Rolled back - database error");
660 qof_instance_mark_clean (inst);
675 g_return_if_fail (
m_conn !=
nullptr);
678 std::string sql {
"SELECT * FROM "};
679 sql += VERSION_TABLE_NAME;
680 auto stmt =
m_conn->create_statement_from_sql(sql);
681 auto result =
m_conn->execute_select_statement (stmt);
682 for (
const auto& row : *result)
684 auto name = row.get_string_at_col (TABLE_COL_NAME);
685 auto version = row.get_int_at_col (VERSION_COL_NAME);
687 m_versions.push_back(std::make_pair(*name, static_cast<unsigned int>(*version)));
708 bool ok =
create_table (VERSION_TABLE_NAME, version_table);
730 if (m_is_pristine_db)
733 auto version = std::find_if(m_versions.begin(), m_versions.end(),
734 [table_name](
const VersionPair& version) {
735 return version.first == table_name; });
736 if (version != m_versions.end())
737 return version->second;
752 uint_t version) noexcept
754 g_return_val_if_fail (version > 0,
false);
756 unsigned int cur_version{0};
757 std::stringstream sql;
758 auto ver_entry = std::find_if(m_versions.begin(), m_versions.end(),
759 [table_name](
const VersionPair& ver) {
760 return ver.first == table_name; });
761 if (ver_entry != m_versions.end())
762 cur_version = ver_entry->second;
763 if (cur_version != version)
765 if (cur_version == 0)
767 sql <<
"INSERT INTO " << VERSION_TABLE_NAME <<
" VALUES('" <<
768 table_name <<
"'," << version <<
")";
769 m_versions.push_back(std::make_pair(table_name, version));
773 sql <<
"UPDATE " << VERSION_TABLE_NAME <<
" SET " <<
774 VERSION_COL_NAME <<
"=" << version <<
" WHERE " <<
775 TABLE_COL_NAME <<
"='" << table_name <<
"'";
776 ver_entry->second = version;
778 auto stmt = create_statement_from_sql(sql.str());
779 auto status = execute_nonselect_statement (stmt);
782 PERR (
"SQL error: %s\n", sql.str().c_str());
793 const EntryVec& col_table) noexcept
795 DEBUG (
"Upgrading %s table\n", table_name.c_str());
797 auto temp_table_name = table_name +
"_new";
798 create_table (temp_table_name, col_table);
799 std::stringstream sql;
800 sql <<
"INSERT INTO " << temp_table_name <<
" SELECT * FROM " << table_name;
801 auto stmt = create_statement_from_sql(sql.str());
802 execute_nonselect_statement(stmt);
805 sql <<
"DROP TABLE " << table_name;
806 stmt = create_statement_from_sql(sql.str());
807 execute_nonselect_statement(stmt);
810 sql <<
"ALTER TABLE " << temp_table_name <<
" RENAME TO " << table_name;
811 stmt = create_statement_from_sql(sql.str());
812 execute_nonselect_statement(stmt);
815 static inline PairVec
817 gpointer pObject,
const EntryVec&
table)
821 for (
auto const& table_row :
table)
823 if (!(table_row->is_autoincr()))
825 table_row->add_to_query (obj_name, pObject, vec);
833 const gpointer pObject,
const EntryVec&
table)
const noexcept
835 g_return_val_if_fail (table_name !=
nullptr,
false);
836 g_return_val_if_fail (obj_name !=
nullptr,
false);
837 g_return_val_if_fail (pObject !=
nullptr,
false);
840 auto sql = std::string{
"SELECT "} +
table[0]->name() +
" FROM " + table_name;
841 auto stmt = create_statement_from_sql(sql.c_str());
842 assert (stmt !=
nullptr);
845 PairVec values{get_object_values(obj_name, pObject,
table)};
848 stmt->add_where_cond(obj_name, values);
849 auto result = execute_select_statement (stmt);
850 return (result !=
nullptr && result->size() > 0);
856 const EntryVec&
table)
const noexcept
858 GncSqlStatementPtr stmt;
860 g_return_val_if_fail (table_name !=
nullptr,
false);
861 g_return_val_if_fail (obj_name !=
nullptr,
false);
862 g_return_val_if_fail (pObject !=
nullptr,
false);
867 stmt = build_insert_statement (table_name, obj_name, pObject,
table);
870 stmt = build_update_statement (table_name, obj_name, pObject,
table);
873 stmt = build_delete_statement (table_name, obj_name, pObject,
table);
878 return (execute_nonselect_statement(stmt) != -1);
884 if (comm ==
nullptr)
return false;
886 auto obe = m_backend_registry.get_object_backend(std::string(inst->
e_type));
887 if (obe && !obe->instance_in_db(
this, inst))
888 return obe->commit(
this, inst);
893 GncSqlBackend::build_insert_statement (
const char* table_name,
896 const EntryVec&
table)
const noexcept
898 GncSqlStatementPtr stmt;
900 std::ostringstream sql;
902 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
903 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
904 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
905 PairVec values{get_object_values(obj_name, pObject,
table)};
907 sql <<
"INSERT INTO " << table_name <<
"(";
908 for (
auto const& col_value : values)
910 if (col_value != *values.begin())
912 sql << col_value.first;
916 for (
const auto& col_value : values)
918 if (col_value != *values.begin())
920 sql << col_value.second;
924 stmt = create_statement_from_sql(sql.str());
929 GncSqlBackend::build_update_statement(
const gchar* table_name,
931 const EntryVec&
table)
const noexcept
933 GncSqlStatementPtr stmt;
934 std::ostringstream sql;
936 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
937 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
938 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
941 PairVec values{get_object_values (obj_name, pObject,
table)};
944 sql <<
"UPDATE " << table_name <<
" SET ";
946 for (
auto const& col_value : values)
948 if (col_value != *values.begin())
950 sql << col_value.first <<
"=" <<
954 stmt = create_statement_from_sql(sql.str());
958 values.erase(values.begin() + 1, values.end());
959 stmt->add_where_cond(obj_name, values);
964 GncSqlBackend::build_delete_statement(
const gchar* table_name,
967 const EntryVec&
table)
const noexcept
969 std::ostringstream sql;
971 g_return_val_if_fail (table_name !=
nullptr,
nullptr);
972 g_return_val_if_fail (obj_name !=
nullptr,
nullptr);
973 g_return_val_if_fail (pObject !=
nullptr,
nullptr);
975 sql <<
"DELETE FROM " << table_name;
976 auto stmt = create_statement_from_sql (sql.str());
980 table[0]->add_to_query (obj_name, pObject, values);
981 PairVec col_values{values[0]};
982 stmt->add_where_cond (obj_name, col_values);
987 GncSqlBackend::ObjectBackendRegistry::ObjectBackendRegistry()
989 register_backend(std::make_shared<GncSqlBookBackend>());
990 register_backend(std::make_shared<GncSqlCommodityBackend>());
991 register_backend(std::make_shared<GncSqlAccountBackend>());
992 register_backend(std::make_shared<GncSqlBudgetBackend>());
993 register_backend(std::make_shared<GncSqlPriceBackend>());
994 register_backend(std::make_shared<GncSqlTransBackend>());
995 register_backend(std::make_shared<GncSqlSplitBackend>());
996 register_backend(std::make_shared<GncSqlSlotsBackend>());
997 register_backend(std::make_shared<GncSqlRecurrenceBackend>());
998 register_backend(std::make_shared<GncSqlSchedXactionBackend>());
999 register_backend(std::make_shared<GncSqlLotsBackend>());
1000 register_backend(std::make_shared<GncSqlBillTermBackend>());
1001 register_backend(std::make_shared<GncSqlCustomerBackend>());
1002 register_backend(std::make_shared<GncSqlEmployeeBackend>());
1003 register_backend(std::make_shared<GncSqlEntryBackend>());
1004 register_backend(std::make_shared<GncSqlInvoiceBackend>());
1005 register_backend(std::make_shared<GncSqlJobBackend>());
1006 register_backend(std::make_shared<GncSqlOrderBackend>());
1007 register_backend(std::make_shared<GncSqlTaxTableBackend>());
1008 register_backend(std::make_shared<GncSqlVendorBackend>());
1012 GncSqlBackend::ObjectBackendRegistry::register_backend(OBEEntry&& entry) noexcept
1014 m_registry.emplace_back(entry);
1018 GncSqlBackend::ObjectBackendRegistry::register_backend(GncSqlObjectBackendPtr obe) noexcept
1020 m_registry.emplace_back(make_tuple(std::string{obe->type()}, obe));
1023 GncSqlObjectBackendPtr
1024 GncSqlBackend::ObjectBackendRegistry::get_object_backend(
const std::string& type)
const 1026 auto entry = std::find_if(m_registry.begin(), m_registry.end(),
1027 [type](
const OBEEntry& entry){
1028 return type == std::get<0>(entry);
1030 if (entry == m_registry.end())
1033 return std::get<1>(*entry);
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 add_columns_to_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Adds one or more columns to an existing table.
bool create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
int xaccAccountTreeForEachTransaction(Account *acc, TransactionCallback proc, void *data)
Traverse all of the transactions in the given account group.
load and save vendor data to SQL
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
a simple price database for gnucash
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
load and save data to SQL
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
load and save accounts data to SQL
VersionVec m_versions
Version number for each table.
void rollback(QofInstance *) override
Object editing has been cancelled.
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
#define DEBUG(format, args...)
Print a debugging message.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
void commit(QofInstance *) override
Object editing is complete and the object should be saved.
void create_tables() noexcept
Create/update all tables in the database.
load and save customer data to SQL
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
bool m_loading
We are performing an initial load.
void commodity_for_postload_processing(gnc_commodity *)
Register a commodity to be committed after loading is complete.
GncSqlConnection * m_conn
SQL connection.
load and save accounts data to SQL
load and save data to SQL
void load(QofBook *, QofBackendLoadType) override
Load the contents of an SQL database into a book.
#define PERR(format, args...)
Log a serious error.
#define ENTER(format, args...)
Print a function entry debugging message.
void sync(QofBook *) override
Save the contents of a book to an SQL database.
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.
error in response from server
const gchar * QofIdType
QofIdType declaration.
load and save accounts data to SQL
load and save order data to SQL
bool save_commodity(gnc_commodity *comm) noexcept
Ensure that a commodity referenced in another object is in fact saved in the database.
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)...
load and save job data to SQL
load and save accounts data to SQL
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
load and save data to SQL
gboolean qof_instance_get_dirty_flag(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object has been modified.
QofBook * m_book
The primary, main open book.
Anchor Scheduled Transaction info in a book.
GncSqlObjectBackendPtr get_object_backend(const std::string &type) const noexcept
Get the GncSqlObjectBackend for the indicated type.
load and save employee data to SQL
load and save data to SQL
Tax Table programming interface.
void init_version_info() noexcept
Initializes DB table version information.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
virtual bool commit_transaction() noexcept=0
Returns TRUE if successful, FALSE if error.
load and save data to SQL
Generic api to store and retrieve preferences.
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...
Business Invoice Interface.
QofIdType e_type
Entity type.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
void begin(QofInstance *) override
An object is about to be edited.
Encapsulate the connection to 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.
Data-passing struct for callbacks to qof_object_foreach() used in GncSqlObjectBackend::write().
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
void connect(GncSqlConnection *conn) noexcept
Connect the backend to a GncSqlConnection.
cannot write to file/directory
load and save entry data to SQL
bool m_is_pristine_db
Are we saving to a new pristine db?
#define LEAVE(format, args...)
Print a function exit debugging message.
Pure virtual class to iterate over a query result set.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
virtual bool begin_transaction() noexcept=0
Returns TRUE if successful, false if error.
load and save data to SQL
virtual bool rollback_transaction() noexcept=0
Returns TRUE if successful, FALSE if error.
load and save invoice data to SQL
virtual bool does_table_exist(const std::string &) const noexcept=0
Returns true if successful.
bool reset_version_info() noexcept
Resets the version table information by removing all version table info.
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
uint_t get_table_version(const std::string &table_name) const noexcept
Returns the version number for a DB table.
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Commodity handling public routines.
void set_error(QofBackendError err)
Set the error value only if there isn't already an error already.
Main SQL backend structure.
void finalize_version_info() noexcept
Finalizes DB table version information.
load and save tax table data to SQL