31 #include <glib/gi18n.h> 34 #include "qofquery-p.h" 35 #include "qofquerycore-p.h" 39 #include <TransactionP.hpp> 42 #include "engine-helpers.h" 47 #include "splint-defs.h" 55 #include <gnc-datetime.hpp> 56 #include "gnc-sql-connection.hpp" 57 #include "gnc-sql-backend.hpp" 58 #include "gnc-sql-object-backend.hpp" 59 #include "gnc-sql-column-table-entry.hpp" 64 #define SIMPLE_QUERY_COMPILATION 1 68 #define TRANSACTION_TABLE "transactions" 69 #define TX_TABLE_VERSION 4 70 #define SPLIT_TABLE "splits" 71 #define SPLIT_TABLE_VERSION 5 82 #define TX_MAX_NUM_LEN 2048 83 #define TX_MAX_DESCRIPTION_LEN 2048 85 static const EntryVec tx_col_table
87 gnc_sql_make_table_entry<CT_GUID>(
"guid", 0, COL_NNUL | COL_PKEY,
"guid"),
88 gnc_sql_make_table_entry<CT_COMMODITYREF>(
"currency_guid", 0, COL_NNUL,
90 gnc_sql_make_table_entry<CT_STRING>(
"num", TX_MAX_NUM_LEN, COL_NNUL,
"num"),
91 gnc_sql_make_table_entry<CT_TIME>(
"post_date", 0, 0,
"post-date"),
92 gnc_sql_make_table_entry<CT_TIME>(
"enter_date", 0, 0,
"enter-date"),
93 gnc_sql_make_table_entry<CT_STRING>(
"description", TX_MAX_DESCRIPTION_LEN,
97 static gpointer get_split_reconcile_state (gpointer pObject);
98 static void set_split_reconcile_state (gpointer pObject, gpointer pValue);
99 static void set_split_lot (gpointer pObject, gpointer pLot);
101 #define SPLIT_MAX_MEMO_LEN 2048 102 #define SPLIT_MAX_ACTION_LEN 2048 104 static const EntryVec split_col_table
106 gnc_sql_make_table_entry<CT_GUID>(
"guid", 0, COL_NNUL | COL_PKEY,
"guid"),
107 gnc_sql_make_table_entry<CT_TXREF>(
"tx_guid", 0, COL_NNUL,
"transaction"),
108 gnc_sql_make_table_entry<CT_ACCOUNTREF>(
"account_guid", 0, COL_NNUL,
110 gnc_sql_make_table_entry<CT_STRING>(
"memo", SPLIT_MAX_MEMO_LEN, COL_NNUL,
112 gnc_sql_make_table_entry<CT_STRING>(
"action", SPLIT_MAX_ACTION_LEN,
114 gnc_sql_make_table_entry<CT_STRING>(
"reconcile_state", 1, COL_NNUL,
116 set_split_reconcile_state),
117 gnc_sql_make_table_entry<CT_TIME>(
"reconcile_date", 0, 0,
119 gnc_sql_make_table_entry<CT_NUMERIC>(
"value", 0, COL_NNUL,
"value"),
120 gnc_sql_make_table_entry<CT_NUMERIC>(
"quantity", 0, COL_NNUL,
"amount"),
121 gnc_sql_make_table_entry<CT_LOTREF>(
"lot_guid", 0, 0,
126 static const EntryVec post_date_col_table
128 gnc_sql_make_table_entry<CT_TIME>(
"post_date", 0, 0,
"post-date"),
131 static const EntryVec account_guid_col_table
133 gnc_sql_make_table_entry<CT_ACCOUNTREF>(
"account_guid", 0, COL_NNUL,
137 static const EntryVec tx_guid_col_table
139 gnc_sql_make_table_entry<CT_GUID>(
"tx_guid", 0, 0,
"guid"),
142 GncSqlTransBackend::GncSqlTransBackend() :
144 TRANSACTION_TABLE, tx_col_table) {}
146 GncSqlSplitBackend::GncSqlSplitBackend() :
148 SPLIT_TABLE, split_col_table) {}
160 get_split_reconcile_state (gpointer pObject)
164 g_return_val_if_fail (pObject != NULL, NULL);
165 g_return_val_if_fail (GNC_IS_SPLIT (pObject), NULL);
173 set_split_reconcile_state (gpointer pObject, gpointer pValue)
175 const gchar* s = (
const gchar*)pValue;
177 g_return_if_fail (pObject != NULL);
178 g_return_if_fail (GNC_IS_SPLIT (pObject));
179 g_return_if_fail (pValue != NULL);
185 set_split_lot (gpointer pObject, gpointer pLot)
190 g_return_if_fail (pObject != NULL);
191 g_return_if_fail (GNC_IS_SPLIT (pObject));
193 if (pLot == NULL)
return;
195 g_return_if_fail (GNC_IS_LOT (pLot));
197 split = GNC_SPLIT (pObject);
198 lot = GNC_LOT (pLot);
207 Split* pSplit = NULL;
209 g_return_val_if_fail (sql_be != NULL, NULL);
211 guid = gnc_sql_load_guid (sql_be, row);
212 if (guid == NULL)
return NULL;
215 PWARN (
"Bad GUID, creating new");
228 gnc_sql_load_object (sql_be, row, GNC_ID_SPLIT, pSplit, split_col_table);
235 PERR (
"A malformed split with id %s was found in the dataset.", guidstr);
243 PERR(
"Split %s created with no account!", guidstr);
248 load_splits_for_transactions (
GncSqlBackend* sql_be, std::string selector)
250 g_return_if_fail (sql_be != NULL);
252 const std::string spkey(split_col_table[0]->name());
253 const std::string sskey(tx_guid_col_table[0]->name());
254 const std::string tpkey(tx_col_table[0]->name());
256 std::string sql(
"SELECT ");
257 if (selector.empty())
259 sql += SPLIT_TABLE
".* FROM " SPLIT_TABLE
" INNER JOIN " 260 TRANSACTION_TABLE
" ON " SPLIT_TABLE
"." + sskey +
" = " 261 TRANSACTION_TABLE
"." + tpkey;
262 selector =
"(SELECT DISTINCT " + tpkey +
" FROM " TRANSACTION_TABLE
")";
265 sql +=
" * FROM " SPLIT_TABLE
" WHERE " + sskey +
" IN " + selector;
268 auto stmt = sql_be->create_statement_from_sql(sql);
271 for (
auto row : *result)
272 load_single_split (sql_be, row);
273 sql =
"SELECT DISTINCT ";
274 sql += spkey +
" FROM " SPLIT_TABLE
" WHERE " + sskey +
" IN " + selector;
286 g_return_val_if_fail (sql_be != NULL, NULL);
288 guid = gnc_sql_load_guid (sql_be, row);
289 if (guid == NULL)
return NULL;
298 gnc_sql_load_object (sql_be, row, GNC_ID_TRANS, pTx, tx_col_table);
304 PERR (
"A malformed transaction with id %s was found in the dataset.", guidstr);
321 gnc_numeric start_bal;
323 gnc_numeric start_cleared_bal;
324 gnc_numeric end_cleared_bal;
325 gnc_numeric start_reconciled_bal;
326 gnc_numeric end_reconciled_bal;
337 query_transactions (
GncSqlBackend* sql_be, std::string selector)
339 g_return_if_fail (sql_be != NULL);
341 const std::string tpkey(tx_col_table[0]->name());
342 std::string sql(
"SELECT * FROM " TRANSACTION_TABLE);
344 if (!selector.empty() && selector[0] ==
'(')
345 sql +=
" WHERE " + tpkey +
" IN " + selector;
346 else if (!selector.empty())
347 sql +=
" WHERE " + selector;
348 auto stmt = sql_be->create_statement_from_sql(sql);
350 if (result->begin() == result->end())
352 PINFO(
"Query %s returned no results", sql.c_str());
359 InstanceVec instances;
360 instances.reserve(result->size());
361 for (
auto row : *result)
363 tx = load_single_tx (sql_be, row);
367 instances.push_back(QOF_INSTANCE(tx));
372 if (!instances.empty())
374 const std::string tpkey(tx_col_table[0]->name());
375 if (!selector.empty() && (selector[0] !=
'('))
377 auto tselector = std::string (
"(SELECT DISTINCT ");
378 tselector += tpkey +
" FROM " TRANSACTION_TABLE
" WHERE " + selector +
")";
379 selector = tselector;
382 load_splits_for_transactions (sql_be, selector);
384 if (selector.empty())
386 selector =
"SELECT DISTINCT ";
387 selector += tpkey +
" FROM " TRANSACTION_TABLE;
395 xaccDisableDataScrubbing();
396 for (
auto instance : instances)
398 xaccEnableDataScrubbing();
414 g_return_if_fail (sql_be != NULL);
419 (void)sql_be->
create_table(TRANSACTION_TABLE, TX_TABLE_VERSION,
421 ok = sql_be->
create_index (
"tx_post_date_index", TRANSACTION_TABLE,
422 post_date_col_table);
425 PERR (
"Unable to create index\n");
428 else if (version < m_version)
437 PINFO (
"Transactions table upgraded from version %d to version %d\n",
444 g_return_if_fail (sql_be !=
nullptr);
452 m_table_name.c_str(), tx_guid_col_table))
453 PERR (
"Unable to create index\n");
455 m_table_name.c_str(),
456 account_guid_col_table))
457 PERR (
"Unable to create index\n");
459 else if (version < SPLIT_TABLE_VERSION)
467 sql_be->
upgrade_table(m_table_name.c_str(), split_col_table);
469 m_table_name.c_str(),
471 PERR (
"Unable to create index\n");
473 m_table_name.c_str(),
474 account_guid_col_table))
475 PERR (
"Unable to create index\n");
477 PINFO (
"Splits table upgraded from version %d to version %d\n", version,
489 delete_split_slots_cb (gpointer data, gpointer user_data)
492 Split* pSplit = GNC_SPLIT (data);
494 g_return_if_fail (data != NULL);
495 g_return_if_fail (GNC_IS_SPLIT (data));
496 g_return_if_fail (user_data != NULL);
498 if (split_info->is_ok)
517 g_return_val_if_fail (sql_be != NULL, FALSE);
518 g_return_val_if_fail (pTx != NULL, FALSE);
521 SPLIT_TABLE, pTx, tx_guid_col_table))
525 split_info.be = sql_be;
526 split_info.is_ok = TRUE;
531 return split_info.is_ok;
549 g_return_val_if_fail (inst != NULL, FALSE);
550 g_return_val_if_fail (sql_be != NULL, FALSE);
552 is_infant = qof_instance_get_infant (inst);
557 else if (sql_be->pristine() || is_infant)
569 qof_instance_set_guid (inst, guid);
573 inst, split_col_table);
588 gboolean is_ok = TRUE;
589 const char* err = NULL;
591 g_return_val_if_fail (sql_be != NULL, FALSE);
592 g_return_val_if_fail (inst != NULL, FALSE);
594 auto pTx = GNC_TRANS(inst);
595 auto is_infant = qof_instance_get_infant (inst);
600 else if (sql_be->pristine() || is_infant)
609 if (op != OP_DB_DELETE)
616 err =
"Commodity save failed: Probably an invalid or missing currency";
627 err =
"Transaction header save failed. Check trace log for SQL errors";
640 err =
"Slots save failed. Check trace log for SQL errors";
648 err =
"Slots delete failed. Check trace log for SQL errors";
652 is_ok = delete_splits (sql_be, pTx);
655 err =
"Split delete failed. Check trace log for SQL errors";
676 PERR (
"Transaction %s dated %s in account %s not saved due to %s.\n",
695 g_return_if_fail (sql_be != NULL);
696 g_return_if_fail (account != NULL);
700 const std::string tpkey(tx_col_table[0]->name());
701 const std::string spkey(split_col_table[0]->name());
702 const std::string stkey(split_col_table[1]->name());
703 const std::string sakey(split_col_table[2]->name());
704 std::string sql(
"(SELECT DISTINCT ");
705 sql += stkey +
" FROM " SPLIT_TABLE
" WHERE " + sakey +
" = '";
706 sql +=
gnc::GUID(*guid).to_string() +
"')";
707 query_transactions (sql_be, sql);
719 g_return_if_fail (sql_be != NULL);
721 auto root = gnc_book_get_root_account (sql_be->book());
724 query_transactions (sql_be,
"");
731 GncSqlStatementPtr stmt;
732 gboolean has_been_run;
740 char reconcile_state;
745 set_acct_bal_account_from_guid (gpointer pObject, gpointer pValue)
750 g_return_if_fail (pObject != NULL);
751 g_return_if_fail (pValue != NULL);
757 set_acct_bal_reconcile_state (gpointer pObject, gpointer pValue)
760 const gchar* s = (
const gchar*)pValue;
762 g_return_if_fail (pObject != NULL);
763 g_return_if_fail (pValue != NULL);
765 bal->reconcile_state = s[0];
769 set_acct_bal_balance (gpointer pObject, gnc_numeric value)
773 g_return_if_fail (pObject != NULL);
775 bal->balance = value;
778 static const EntryVec acct_balances_col_table
780 gnc_sql_make_table_entry<CT_GUID>(
"account_guid", 0, 0,
nullptr,
782 gnc_sql_make_table_entry<CT_STRING>(
"reconcile_state", 1, 0,
nullptr,
784 gnc_sql_make_table_entry<CT_NUMERIC>(
"quantity", 0, 0,
nullptr,
793 gpointer pObject)
const noexcept
795 g_return_if_fail (sql_be != NULL);
796 g_return_if_fail (pObject != NULL);
798 auto val = row.get_string_at_col (m_col_name);
803 Transaction *tx =
nullptr;
808 std::string tpkey(tx_col_table[0]->name());
811 std::string sql = tpkey +
" = '" + *val +
"'";
817 set_parameter (pObject, tx, get_setter(obj_name), m_gobj_param_name);
824 add_objectref_guid_to_table(vec);
829 const gpointer pObject,
830 PairVec& vec)
const noexcept
832 add_objectref_guid_to_query(obj_name, pObject, vec);
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 create_table(const std::string &table_name, const EntryVec &col_table) const noexcept
Creates a table in the database.
Transaction * xaccMallocTransaction(QofBook *book)
The xaccMallocTransaction() will malloc memory and initialize it.
bool set_table_version(const std::string &table_name, uint_t version) noexcept
Registers the version for a table.
const EntryVec & m_col_table
The front-end QofIdType.
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr &stmt) const noexcept
Executes an SQL SELECT statement and returns the result rows.
const GncGUID * qof_instance_get_guid(gconstpointer inst)
Return the GncGUID of this instance.
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction's split list.
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
void create_tables(GncSqlBackend *) override
Creates the transaction and split tables.
void gnc_sql_slots_load_for_sql_subquery(GncSqlBackend *sql_be, const std::string subquery, BookLookupFn lookup_fn)
gnc_sql_slots_load_for_sql_subquery - Loads slots for all objects whose guid is supplied by a subquer...
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Print an informational note.
load and save data to SQL
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
load and save accounts data to SQL
GncGUID guid_new_return(void)
Generate a new id.
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
gboolean string_to_guid(const gchar *string, GncGUID *guid)
Given a string, replace the given guid with the parsed one unless the given value is null...
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
gboolean gnc_sql_slots_save(GncSqlBackend *sql_be, const GncGUID *guid, gboolean is_infant, QofInstance *inst)
gnc_sql_slots_save - Saves slots for an object to the db.
void add_to_query(QofIdTypeConst obj_name, void *pObject, PairVec &vec) const noexcept override
Add a pair of the table column heading and object's value's string representation to a PairVec; used ...
void gnc_lot_add_split(GNCLot *lot, Split *split)
Adds a split to this lot.
void load_all(GncSqlBackend *) override
Loads all transactions.
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
gchar * guid_to_string_buff(const GncGUID *guid, gchar *str)
The guid_to_string_buff() routine puts a null-terminated string encoding of the id into the memory po...
void xaccTransScrubPostedDate(Transaction *trans)
Changes Transaction date_posted timestamps from 00:00 local to 11:00 UTC.
#define PERR(format, args...)
Log a serious error.
void(* QofSetterFunc)(gpointer, gpointer)
The QofSetterFunc defines an function pointer for parameter setters.
void create_tables(GncSqlBackend *) override
Conditionally create or update a database table from m_col_table.
#define PWARN(format, args...)
Log a warning.
void load(const GncSqlBackend *sql_be, GncSqlRow &row, QofIdTypeConst obj_name, void *pObject) const noexcept override
Load a value into an object from the database row.
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
The xaccTransLookup() subroutine will return the transaction associated with the given id...
convert single-entry accounts to clean double-entry
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id, or NULL if there is no such split.
Account handling public routines.
bool save_commodity(gnc_commodity *comm) noexcept
Ensure that a commodity referenced in another object is in fact saved in the database.
Row of SQL Query results.
void upgrade_table(const std::string &table_name, const EntryVec &col_table) noexcept
Upgrades a table to a new structure.
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
#define GUID_ENCODING_LENGTH
Number of characters needed to encode a guid as a string not including the null terminator.
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
Commits a split to the database.
load and save data to SQL
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
The QofAccessFunc defines an arbitrary function pointer for access functions.
Encapsulates per-class table schema with functions to load, create a table, commit a changed front-en...
bool commit(GncSqlBackend *sql_be, QofInstance *inst) override
UPDATE/INSERT a single instance of m_type_name into the database.
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.
Split * xaccMallocSplit(QofBook *book)
Constructor.
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...
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
gboolean gnc_sql_slots_delete(GncSqlBackend *sql_be, const GncGUID *guid)
gnc_sql_slots_delete - Deletes slots for an object from the db.
void add_to_table(ColVec &vec) const noexcept override
Add a GncSqlColumnInfo structure for the column type to a ColVec.
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
API for Transactions and Splits (journal entries)
The type used to store guids in C.
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.
Structure to hold start/end balances for each account.
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Main SQL backend structure.
void gnc_sql_transaction_load_tx_for_account(GncSqlBackend *sql_be, Account *account)
Loads all transactions which have splits for a specific account.
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...