GnuCash c935c2f+
Loading...
Searching...
No Matches
Public Member Functions
GncDbiSqlConnection Class Reference

Encapsulate a libdbi dbi_conn connection. More...

#include <gnc-dbisqlconnection.hpp>

Inheritance diagram for GncDbiSqlConnection:
GncSqlConnection

Public Member Functions

 GncDbiSqlConnection (DbType type, QofBackend *qbe, dbi_conn conn, SessionOpenMode mode)
 
GncSqlResultPtr execute_select_statement (const GncSqlStatementPtr &) noexcept override
 
int execute_nonselect_statement (const GncSqlStatementPtr &) noexcept override
 Returns false if error.
 
GncSqlStatementPtr create_statement_from_sql (const std::string &) const noexcept override
 
bool does_table_exist (const std::string &) const noexcept override
 Returns true if successful.
 
bool begin_transaction () noexcept override
 Returns TRUE if successful, false if error.
 
bool rollback_transaction () noexcept override
 Returns TRUE if successful, FALSE if error.
 
bool commit_transaction () noexcept override
 Returns TRUE if successful, FALSE if error.
 
bool create_table (const std::string &, const ColVec &) const noexcept override
 Returns TRUE if successful, FALSE if error.
 
bool create_index (const std::string &, const std::string &, const EntryVec &) const noexcept override
 Returns TRUE if successful, FALSE if error.
 
bool add_columns_to_table (const std::string &, const ColVec &) const noexcept override
 Returns TRUE if successful, FALSE if error.
 
std::string quote_string (const std::string &) const noexcept override
 
int dberror () const noexcept override
 Get the connection error value.
 
QofBackendqbe () const noexcept
 
dbi_conn conn () const noexcept
 
void set_error (QofBackendError error, unsigned int repeat, bool retry) noexcept override
 
void init_error () noexcept
 
bool verify () noexcept override
 Check if the dbi connection is valid.
 
bool retry_connection (const char *msg) noexcept override
 
bool table_operation (TableOpType op) noexcept
 Perform a specified SQL operation on every table in a database.
 
std::string add_columns_ddl (const std::string &table_name, const ColVec &info_vec) const noexcept
 
bool drop_indexes () noexcept
 
- Public Member Functions inherited from GncSqlConnection
virtual ~GncSqlConnection ()=default
 Returns NULL if error.
 

Detailed Description

Encapsulate a libdbi dbi_conn connection.

Definition at line 41 of file gnc-dbisqlconnection.hpp.

Constructor & Destructor Documentation

◆ GncDbiSqlConnection()

GncDbiSqlConnection::GncDbiSqlConnection ( DbType  type,
QofBackend qbe,
dbi_conn  conn,
SessionOpenMode  mode 
)

Definition at line 79 of file gnc-dbisqlconnection.cpp.

80 :
81 m_qbe{qbe}, m_conn{conn},
82 m_provider{type == DbType::DBI_SQLITE ?
83 make_dbi_provider<DbType::DBI_SQLITE>() :
84 type == DbType::DBI_MYSQL ?
85 make_dbi_provider<DbType::DBI_MYSQL>() :
86 make_dbi_provider<DbType::DBI_PGSQL>()},
87 m_conn_ok{true}, m_last_error{ERR_BACKEND_NO_ERR}, m_error_repeat{0},
88 m_retry{false}, m_sql_savepoint{0}, m_readonly{false}
89{
90 if (mode == SESSION_READ_ONLY)
91 m_readonly = true;
92 else if (!lock_database(mode == SESSION_BREAK_LOCK))
93 throw std::runtime_error("Failed to lock database!");
94 if (!check_and_rollback_failed_save())
95 {
96 unlock_database();
97 throw std::runtime_error("A failed safe-save was detected and rolling it back failed.");
98 }
99}
@ SESSION_READ_ONLY
Create a new store at the URI even if a store already exists there.
Definition qofsession.h:128
@ SESSION_BREAK_LOCK
Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked.
Definition qofsession.h:130

◆ ~GncDbiSqlConnection()

GncDbiSqlConnection::~GncDbiSqlConnection ( )
override

Definition at line 247 of file gnc-dbisqlconnection.cpp.

248{
249 if (m_conn)
250 {
251 unlock_database();
252 dbi_conn_close(m_conn);
253 m_conn = nullptr;
254 }
255}

Member Function Documentation

◆ add_columns_ddl()

std::string GncDbiSqlConnection::add_columns_ddl ( const std::string &  table_name,
const ColVec &  info_vec 
) const
noexcept

Definition at line 756 of file gnc-dbisqlconnection.cpp.

758{
759 std::string ddl;
760
761 ddl += "ALTER TABLE " + table_name;
762 for (auto const& info : info_vec)
763 {
764 if (info != *info_vec.begin())
765 {
766 ddl += ", ";
767 }
768 ddl += "ADD COLUMN ";
769 m_provider->append_col_def (ddl, info);
770 }
771 return ddl;
772}

◆ add_columns_to_table()

bool GncDbiSqlConnection::add_columns_to_table ( const std::string &  ,
const ColVec &   
) const
overridevirtualnoexcept

Returns TRUE if successful, FALSE if error.

Implements GncSqlConnection.

Definition at line 518 of file gnc-dbisqlconnection.cpp.

521{
522 auto ddl = add_columns_ddl(table_name, info_vec);
523 if (ddl.empty())
524 return false;
525
526 DEBUG ("SQL: %s\n", ddl.c_str());
527 auto result = dbi_conn_query (m_conn, ddl.c_str());
528 auto status = dbi_result_free (result);
529 if (status < 0)
530 {
531 PERR( "Error in dbi_result_free() result\n" );
533 }
534
535 return true;
536}
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
Definition qofbackend.h:71
#define DEBUG(format, args...)
Print a debugging message.
Definition qoflog.h:264
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244

◆ begin_transaction()

bool GncDbiSqlConnection::begin_transaction ( )
overridevirtualnoexcept

Returns TRUE if successful, false if error.

Implements GncSqlConnection.

Definition at line 335 of file gnc-dbisqlconnection.cpp.

336{
337 dbi_result result;
338
339 DEBUG ("BEGIN\n");
340
341 if (!verify ())
342 {
343 PERR ("gnc_dbi_verify_conn() failed\n");
345 return false;
346 }
347
348 do
349 {
350 init_error ();
351 if (m_sql_savepoint == 0)
352 result = dbi_conn_queryf (m_conn, "BEGIN");
353 else
354 {
355 std::ostringstream savepoint;
356 savepoint << "savepoint_" << m_sql_savepoint;
357 result = dbi_conn_queryf(m_conn, "SAVEPOINT %s",
358 savepoint.str().c_str());
359 }
360 }
361 while (m_retry);
362
363 if (!result)
364 {
365 PERR ("BEGIN transaction failed()\n");
367 return false;
368 }
369 if (dbi_result_free (result) < 0)
370 {
371 PERR ("Error in dbi_result_free() result\n");
373 return false;
374 }
375 ++m_sql_savepoint;
376 return true;
377}
bool verify() noexcept override
Check if the dbi connection is valid.

◆ commit_transaction()

bool GncDbiSqlConnection::commit_transaction ( )
overridevirtualnoexcept

Returns TRUE if successful, FALSE if error.

Implements GncSqlConnection.

Definition at line 413 of file gnc-dbisqlconnection.cpp.

414{
415 DEBUG ("COMMIT\n");
416 if (m_sql_savepoint == 0) return false;
417 dbi_result result;
418 if (m_sql_savepoint == 1)
419 result = dbi_conn_queryf (m_conn, "COMMIT");
420 else
421 {
422 std::ostringstream savepoint;
423 savepoint << "savepoint_" << m_sql_savepoint - 1;
424 result = dbi_conn_queryf(m_conn, "RELEASE SAVEPOINT %s",
425 savepoint.str().c_str());
426 }
427
428 if (!result)
429 {
430 PERR ("Error in conn_commit_transaction()\n");
432 return false;
433 }
434
435 if (dbi_result_free (result) < 0)
436 {
437 PERR ("Error in dbi_result_free() result\n");
439 return false;
440 }
441 --m_sql_savepoint;
442 return true;
443}

◆ conn()

dbi_conn GncDbiSqlConnection::conn ( ) const
inlinenoexcept

Definition at line 66 of file gnc-dbisqlconnection.hpp.

66{ return m_conn; }

◆ create_index()

bool GncDbiSqlConnection::create_index ( const std::string &  ,
const std::string &  ,
const EntryVec &   
) const
overridevirtualnoexcept

Returns TRUE if successful, FALSE if error.

Implements GncSqlConnection.

Definition at line 498 of file gnc-dbisqlconnection.cpp.

501{
502 auto ddl = create_index_ddl (this, index_name, table_name, col_table);
503 if (ddl.empty())
504 return false;
505 DEBUG ("SQL: %s\n", ddl.c_str());
506 auto result = dbi_conn_query (m_conn, ddl.c_str());
507 auto status = dbi_result_free (result);
508 if (status < 0)
509 {
510 PERR ("Error in dbi_result_free() result\n");
512 }
513
514 return true;
515}

◆ create_statement_from_sql()

GncSqlStatementPtr GncDbiSqlConnection::create_statement_from_sql ( const std::string &  sql) const
overridevirtualnoexcept

Implements GncSqlConnection.

Definition at line 321 of file gnc-dbisqlconnection.cpp.

323{
324 return std::unique_ptr<GncSqlStatement>{new GncDbiSqlStatement (sql)};
325}

◆ create_table()

bool GncDbiSqlConnection::create_table ( const std::string &  ,
const ColVec &   
) const
overridevirtualnoexcept

Returns TRUE if successful, FALSE if error.

Implements GncSqlConnection.

Definition at line 447 of file gnc-dbisqlconnection.cpp.

449{
450 std::string ddl;
451 unsigned int col_num = 0;
452
453 ddl += "CREATE TABLE " + table_name + "(";
454 for (auto const& info : info_vec)
455 {
456 if (col_num++ != 0)
457 {
458 ddl += ", ";
459 }
460 m_provider->append_col_def (ddl, info);
461 }
462 ddl += ")";
463
464 if (ddl.empty())
465 return false;
466
467 DEBUG ("SQL: %s\n", ddl.c_str());
468 auto result = dbi_conn_query (m_conn, ddl.c_str());
469 auto status = dbi_result_free (result);
470 if (status < 0)
471 {
472 PERR ("Error in dbi_result_free() result\n");
474 }
475
476 return true;
477}

◆ dberror()

int GncDbiSqlConnection::dberror ( ) const
inlineoverridevirtualnoexcept

Get the connection error value.

If not 0 will normally be meaningless outside of implementation code.

Implements GncSqlConnection.

Definition at line 63 of file gnc-dbisqlconnection.hpp.

63 {
64 return dbi_conn_error(m_conn, nullptr); }

◆ does_table_exist()

bool GncDbiSqlConnection::does_table_exist ( const std::string &  ) const
overridevirtualnoexcept

Returns true if successful.

Implements GncSqlConnection.

Definition at line 328 of file gnc-dbisqlconnection.cpp.

330{
331 return ! m_provider->get_table_list(m_conn, table_name).empty();
332}

◆ drop_indexes()

bool GncDbiSqlConnection::drop_indexes ( )
noexcept

Definition at line 739 of file gnc-dbisqlconnection.cpp.

740{
741 auto index_list = m_provider->get_index_list (m_conn);
742 for (auto index : index_list)
743 {
744 const char* errmsg;
745 m_provider->drop_index (m_conn, index);
746 if (DBI_ERROR_NONE != dbi_conn_error (m_conn, &errmsg))
747 {
748 PERR("Failed to drop indexes %s", errmsg);
749 return false;
750 }
751 }
752 return true;
753}

◆ execute_nonselect_statement()

int GncDbiSqlConnection::execute_nonselect_statement ( const GncSqlStatementPtr &  )
overridevirtualnoexcept

Returns false if error.

Implements GncSqlConnection.

Definition at line 284 of file gnc-dbisqlconnection.cpp.

286{
287 dbi_result result;
288
289 DEBUG ("SQL: %s\n", stmt->to_sql());
290 do
291 {
292 init_error ();
293 result = dbi_conn_query (m_conn, stmt->to_sql());
294 }
295 while (m_retry);
296 if (result == nullptr && m_last_error)
297 {
298 PERR ("Error executing SQL %s\n", stmt->to_sql());
299 if(m_last_error)
300 m_qbe->set_error(m_last_error);
301 else
303 return -1;
304 }
305 if (!result)
306 return 0;
307 auto num_rows = (gint)dbi_result_get_numrows_affected (result);
308 auto status = dbi_result_free (result);
309 if (status < 0)
310 {
311 PERR ("Error in dbi_result_free() result\n");
312 if(m_last_error)
313 m_qbe->set_error(m_last_error);
314 else
316 }
317 return num_rows;
318}
void set_error(QofBackendError err)
Set the error value only if there isn't already an error already.

◆ execute_select_statement()

GncSqlResultPtr GncDbiSqlConnection::execute_select_statement ( const GncSqlStatementPtr &  stmt)
overridevirtualnoexcept

Implements GncSqlConnection.

Definition at line 258 of file gnc-dbisqlconnection.cpp.

260{
261 dbi_result result;
262
263 DEBUG ("SQL: %s\n", stmt->to_sql());
264 auto locale = gnc_push_locale (LC_NUMERIC, "C");
265 do
266 {
267 init_error ();
268 result = dbi_conn_query (m_conn, stmt->to_sql());
269 }
270 while (m_retry);
271 if (result == nullptr)
272 {
273 PERR ("Error executing SQL %s\n", stmt->to_sql());
274 if(m_last_error)
275 m_qbe->set_error(m_last_error);
276 else
278 }
279 gnc_pop_locale (LC_NUMERIC, locale);
280 return GncSqlResultPtr(new GncDbiSqlResult (this, result));
281}
An iterable wrapper for dbi_result; allows using C++11 range for.
Pure virtual class to iterate over a query result set.

◆ init_error()

void GncDbiSqlConnection::init_error ( )
inlinenoexcept

Definition at line 74 of file gnc-dbisqlconnection.hpp.

75 {
76 set_error(ERR_BACKEND_NO_ERR, 0, false);
77 }

◆ qbe()

QofBackend * GncDbiSqlConnection::qbe ( ) const
inlinenoexcept

Definition at line 65 of file gnc-dbisqlconnection.hpp.

65{ return m_qbe; }

◆ quote_string()

std::string GncDbiSqlConnection::quote_string ( const std::string &  unquoted_str) const
overridevirtualnoexcept

Implements GncSqlConnection.

Definition at line 539 of file gnc-dbisqlconnection.cpp.

541{
542 char* quoted_str;
543
544 dbi_conn_quote_string_copy (m_conn, unquoted_str.c_str(),
545 &quoted_str);
546 if (quoted_str == nullptr)
547 return std::string{""};
548 std::string retval{quoted_str};
549 free(quoted_str);
550 return retval;
551}

◆ retry_connection()

bool GncDbiSqlConnection::retry_connection ( const char *  msg)
overridevirtualnoexcept

Implements GncSqlConnection.

Definition at line 576 of file gnc-dbisqlconnection.cpp.

578{
579 while (m_retry && m_error_repeat <= DBI_MAX_CONN_ATTEMPTS)
580 {
581 m_conn_ok = false;
582 if (dbi_conn_connect(m_conn) == 0)
583 {
584 init_error();
585 m_conn_ok = true;
586 return true;
587 }
588#ifdef G_OS_WIN32
589 const guint backoff_msecs = 1;
590 Sleep (backoff_msecs * 2 << ++m_error_repeat);
591#else
592 const guint backoff_usecs = 1000;
593 usleep (backoff_usecs * 2 << ++m_error_repeat);
594#endif
595 PINFO ("DBI error: %s - Reconnecting...\n", msg);
596
597 }
598 PERR ("DBI error: %s - Giving up after %d consecutive attempts.\n", msg,
599 DBI_MAX_CONN_ATTEMPTS);
600 m_conn_ok = false;
601 return false;
602}
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256

◆ rollback_transaction()

bool GncDbiSqlConnection::rollback_transaction ( )
overridevirtualnoexcept

Returns TRUE if successful, FALSE if error.

Implements GncSqlConnection.

Definition at line 380 of file gnc-dbisqlconnection.cpp.

381{
382 DEBUG ("ROLLBACK\n");
383 if (m_sql_savepoint == 0) return false;
384 dbi_result result;
385 if (m_sql_savepoint == 1)
386 result = dbi_conn_query (m_conn, "ROLLBACK");
387 else
388 {
389 std::ostringstream savepoint;
390 savepoint << "savepoint_" << m_sql_savepoint - 1;
391 result = dbi_conn_queryf(m_conn, "ROLLBACK TO SAVEPOINT %s",
392 savepoint.str().c_str());
393 }
394 if (!result)
395 {
396 PERR ("Error in conn_rollback_transaction()\n");
398 return false;
399 }
400
401 if (dbi_result_free (result) < 0)
402 {
403 PERR ("Error in dbi_result_free() result\n");
405 return false;
406 }
407
408 --m_sql_savepoint;
409 return true;
410}

◆ set_error()

void GncDbiSqlConnection::set_error ( QofBackendError  error,
unsigned int  repeat,
bool  retry 
)
inlineoverridevirtualnoexcept

Implements GncSqlConnection.

Definition at line 67 of file gnc-dbisqlconnection.hpp.

69 {
70 m_last_error = error;
71 m_error_repeat = repeat;
72 m_retry = retry;
73 }

◆ table_operation()

bool GncDbiSqlConnection::table_operation ( TableOpType  op)
noexcept

Perform a specified SQL operation on every table in a database.

Possible operations are:

  • drop: to DROP all tables from the database
  • empty: to DELETE all records from each table in the database.
  • backup: Rename every table from "name" to "name_back"
  • drop_backup: DROP the backup tables.
  • rollback: DROP the new table "name" and rename "name_back" to "name", restoring the database to its previous state.

The intent of the last two is to be able to move an existing table aside, query its contents with a transformation (in 2.4.x this is already done as the contents are loaded completely when a Qof session is started), save them to a new table according to a new database format, and finally drop the backup table; if there's an error during the process, rollback allows returning the table to its original state.

Parameters
sql_connThe sql connection (via dbi) to which the transactions will be sent
table_namessStrVec of tables to operate on.
opThe operation to perform.
Returns
Success (TRUE) or failure.

Definition at line 671 of file gnc-dbisqlconnection.cpp.

672{
673 auto backup_tables = m_provider->get_table_list(m_conn, "%_back");
674 auto all_tables = m_provider->get_table_list(m_conn, "");
675 /* No operations on the lock table */
676 auto new_end = std::remove(all_tables.begin(), all_tables.end(), lock_table);
677 all_tables.erase(new_end, all_tables.end());
678 StrVec data_tables;
679 data_tables.reserve(all_tables.size() - backup_tables.size());
680 std::set_difference(all_tables.begin(), all_tables.end(),
681 backup_tables.begin(), backup_tables.end(),
682 std::back_inserter(data_tables));
683 switch(op)
684 {
685 case backup:
686 if (!backup_tables.empty())
687 {
688 PERR("Unable to backup database, an existing backup is present.");
690 return false;
691 }
692 for (auto table : data_tables)
693 if (!rename_table(table, table +"_back"))
694 return false; /* Error, trigger rollback. */
695 break;
696 case drop_backup:
697 for (auto table : backup_tables)
698 {
699 auto data_table = table.substr(0, table.find("_back"));
700 if (std::find(data_tables.begin(), data_tables.end(),
701 data_table) != data_tables.end())
702 drop_table(table); /* Other table exists, OK. */
703 else /* No data table, restore the backup */
704 rename_table(table, data_table);
705 }
706 break;
707 case rollback:
708 for (auto table : backup_tables)
709 {
710 auto data_table = table.substr(0, table.find("_back"));
711 if (std::find(data_tables.begin(), data_tables.end(),
712 data_table) != data_tables.end())
713 drop_table(data_table); /* Other table exists, OK. */
714 rename_table(table, data_table);
715 }
716 break;
717 case recover:
718 for (auto table : backup_tables)
719 {
720 auto data_table = table.substr(0, table.find("_back"));
721 if (std::find(data_tables.begin(), data_tables.end(),
722 data_table) != data_tables.end())
723 {
724 if (!merge_tables(data_table, table))
725 return false;
726 }
727 else
728 {
729 if (!rename_table(table, data_table))
730 return false;
731 }
732 }
733 break;
734 }
735 return true;
736}
@ ERR_BACKEND_DATA_CORRUPT
data in db is corrupt
Definition qofbackend.h:70

◆ verify()

bool GncDbiSqlConnection::verify ( )
overridevirtualnoexcept

Check if the dbi connection is valid.

If not attempt to re-establish it Returns TRUE if there is a valid connection in the end or FALSE otherwise

Implements GncSqlConnection.

Definition at line 558 of file gnc-dbisqlconnection.cpp.

559{
560 if (m_conn_ok)
561 return true;
562
563 /* We attempt to connect only once here. The error function will
564 * automatically re-attempt up until DBI_MAX_CONN_ATTEMPTS time to connect
565 * if this call fails. After all these attempts, conn_ok will indicate if
566 * there is a valid connection or not.
567 */
568 init_error ();
569 m_conn_ok = true;
570 (void)dbi_conn_connect (m_conn);
571
572 return m_conn_ok;
573}

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