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" 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" 71 using StrVec = std::vector<std::string>;
73 static std::string empty_string{};
74 static 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}
89 GncSqlBackend::~GncSqlBackend()
97 if (m_conn !=
nullptr && m_conn != conn)
99 finalize_version_info();
104 GncSqlBackend::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());
128 GncSqlBackend::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());
140 GncSqlBackend::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);
198 GncSqlBackend::update_progress(
double pct)
const noexcept
200 if (m_percentage !=
nullptr)
201 (m_percentage) (
nullptr, pct);
205 GncSqlBackend::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);
222 static const StrVec fixed_load_order
223 { GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, GNC_ID_TRANS };
226 static const StrVec business_fixed_load_order =
227 { GNC_ID_BILLTERM, GNC_ID_TAXTABLE, GNC_ID_INVOICE };
230 GncSqlBackend::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;
276 scrub_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);
368 GncSqlBackend::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);
394 GncSqlBackend::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);
408 write_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);
428 GncSqlBackend::write_transactions()
430 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
434 gnc_book_get_root_account (
m_book), write_tx, &data);
435 update_progress(101.0);
440 GncSqlBackend::write_template_transactions()
442 auto obe = m_backend_registry.get_object_backend(GNC_ID_TRANS);
448 update_progress(101.0);
455 GncSqlBackend::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);
569 GncSqlObjectBackendPtr
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);
818 static inline PairVec
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);
896 GncSqlBackend::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());
932 GncSqlBackend::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);
967 GncSqlBackend::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);
990 GncSqlBackend::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>());
1015 GncSqlBackend::ObjectBackendRegistry::register_backend(OBEEntry&& entry) noexcept
1017 m_registry.emplace_back(entry);
1021 GncSqlBackend::ObjectBackendRegistry::register_backend(GncSqlObjectBackendPtr obe) noexcept
1023 m_registry.emplace_back(make_tuple(std::string{obe->type()}, obe));
1026 GncSqlObjectBackendPtr
1027 GncSqlBackend::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);
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.
void xaccLogDisable(void)
document me
#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...
API for the transaction logger.
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.
void xaccLogEnable(void)
document me
Main SQL backend structure.
void finalize_version_info() noexcept
Finalizes DB table version information.
load and save tax table data to SQL