r19728 - gnucash/trunk/src - Bug #630770 - Crash when connection lost using db

Geert Janssens gjanssens at code.gnucash.org
Fri Oct 29 04:39:29 EDT 2010


Author: gjanssens
Date: 2010-10-29 04:39:29 -0400 (Fri, 29 Oct 2010)
New Revision: 19728
Trac: http://svn.gnucash.org/trac/changeset/19728

Modified:
   gnucash/trunk/src/backend/dbi/gnc-backend-dbi.c
   gnucash/trunk/src/backend/sql/gnc-backend-sql.c
   gnucash/trunk/src/engine/Split.c
Log:
Bug #630770 - Crash when connection lost using db

Modified: gnucash/trunk/src/backend/dbi/gnc-backend-dbi.c
===================================================================
--- gnucash/trunk/src/backend/dbi/gnc-backend-dbi.c	2010-10-29 08:32:07 UTC (rev 19727)
+++ gnucash/trunk/src/backend/dbi/gnc-backend-dbi.c	2010-10-29 08:39:29 UTC (rev 19728)
@@ -161,6 +161,7 @@
     dbi_conn conn;
     /*@ observer @*/
     provider_functions_t* provider;
+    gboolean conn_ok;       // Used by the error handler routines to flag if the connection is ok to use
     gint last_error;        // Code of the last error that occurred. This is set in the error callback function
     gint error_repeat;      // Used in case of transient errors. After such error, another attempt at the
     // original call is allowed. error_repeat tracks the number of attempts and can
@@ -193,6 +194,27 @@
     gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_ERR, 0, FALSE );
 }
 
+/* Check if the dbi connection is valid. If not attempt to re-establish it
+ * Returns TRUE is there is a valid connection in the end or FALSE otherwise
+ */
+static gboolean
+gnc_dbi_verify_conn( GncDbiSqlConnection* dbi_conn )
+{
+    if ( dbi_conn->conn_ok )
+        return TRUE;
+
+    /* We attempt to connect only once here. The error function will automatically
+     * re-attempt up until DBI_MAX_CONN_ATTEMPTS time to connect if this call fails.
+     * After all these attempts, conn_ok will indicate if there is a valid connection
+     * or not.
+     */
+    gnc_dbi_init_error( dbi_conn );
+    dbi_conn->conn_ok = TRUE;
+    (void)dbi_conn_connect( dbi_conn->conn );
+
+    return dbi_conn->conn_ok;
+}
+
 /* ================================================================= */
 
 static void
@@ -330,16 +352,24 @@
     }
     else if ( err_num == 2006 )     // Server has gone away
     {
-        if (dbi_conn->error_repeat > DBI_MAX_CONN_ATTEMPTS )
+        PINFO( "DBI error: %s - Reconnecting...\n", msg );
+        gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
+        dbi_conn->conn_ok = TRUE;
+        (void)dbi_conn_connect( conn );
+    }
+    else if ( err_num == 2003 )     // Unable to connect
+    {
+        if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS )
         {
-            PERR( "DBI error: %s - Failed to reconnect after %d attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
+            PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
             gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE );
+            dbi_conn->conn_ok = FALSE;
         }
         else
         {
             PINFO( "DBI error: %s - Reconnecting...\n", msg );
-            gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
-
+            gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE );
+            dbi_conn->conn_ok = TRUE;
             (void)dbi_conn_connect( conn );
         }
     }
@@ -571,6 +601,30 @@
         be->exists = FALSE;
         gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_SUCH_DB, 0, FALSE );
     }
+    else if ( g_strrstr( msg, "server closed the connection unexpectedly" ) ) // Connection lost
+    {
+        PINFO( "DBI error: %s - Reconnecting...\n", msg );
+        gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
+        dbi_conn->conn_ok = TRUE;
+        (void)dbi_conn_connect( conn );
+    }
+    else if ( g_str_has_prefix( msg, "connection pointer is NULL" ) ||
+              g_str_has_prefix(msg, "could not connect to server" ) )     // No connection
+    {
+        if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS )
+        {
+            PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
+            gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE );
+            dbi_conn->conn_ok = FALSE;
+        }
+        else
+        {
+            PINFO( "DBI error: %s - Reconnecting...\n", msg );
+            gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE );
+            dbi_conn->conn_ok = TRUE;
+            (void)dbi_conn_connect( conn );
+        }
+    }
     else
     {
         PERR( "DBI error: %s\n", msg );
@@ -1575,9 +1629,17 @@
     GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
     dbi_result result;
     gint status;
+    gboolean success = FALSE;
 
     DEBUG( "BEGIN\n" );
 
+    if ( !gnc_dbi_verify_conn (dbi_conn) )
+    {
+        PERR( "gnc_dbi_verify_conn() failed\n" );
+        qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
+        return FALSE;
+    }
+
     do
     {
         gnc_dbi_init_error( dbi_conn );
@@ -1585,14 +1647,20 @@
     }
     while ( dbi_conn->retry );
 
+    success = ( result != NULL );
     status = dbi_result_free( result );
     if ( status < 0 )
     {
         PERR( "Error in dbi_result_free() result\n" );
         qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
     }
+    if ( !success )
+    {
+        PERR( "BEGIN transaction failed()\n" );
+        qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
+    }
 
-    return TRUE;
+    return success;
 }
 
 static gboolean
@@ -1601,17 +1669,25 @@
     GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
     dbi_result result;
     gint status;
+    gboolean success = FALSE;
 
     DEBUG( "ROLLBACK\n" );
     result = dbi_conn_queryf( dbi_conn->conn, "ROLLBACK" );
+    success = ( result != NULL );
+
     status = dbi_result_free( result );
     if ( status < 0 )
     {
         PERR( "Error in dbi_result_free() result\n" );
         qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
     }
+    if ( !success )
+    {
+        PERR( "Error in conn_rollback_transaction()\n" );
+        qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
+    }
 
-    return TRUE;
+    return success;
 }
 
 static gboolean
@@ -1620,17 +1696,25 @@
     GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
     dbi_result result;
     gint status;
+    gboolean success = FALSE;
 
     DEBUG( "COMMIT\n" );
     result = dbi_conn_queryf( dbi_conn->conn, "COMMIT" );
+    success = ( result != NULL );
+
     status = dbi_result_free( result );
     if ( status < 0 )
     {
         PERR( "Error in dbi_result_free() result\n" );
         qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
     }
+    if ( !success )
+    {
+        PERR( "Error in conn_commit_transaction()\n" );
+        qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
+    }
 
-    return TRUE;
+    return success;
 }
 
 static /*@ null @*/ gchar*
@@ -2171,6 +2255,7 @@
     dbi_conn->qbe = qbe;
     dbi_conn->conn = conn;
     dbi_conn->provider = provider;
+    dbi_conn->conn_ok = TRUE;
 
     gnc_dbi_init_error(dbi_conn);
 

Modified: gnucash/trunk/src/backend/sql/gnc-backend-sql.c
===================================================================
--- gnucash/trunk/src/backend/sql/gnc-backend-sql.c	2010-10-29 08:32:07 UTC (rev 19727)
+++ gnucash/trunk/src/backend/sql/gnc-backend-sql.c	2010-10-29 08:39:29 UTC (rev 19728)
@@ -494,13 +494,16 @@
     be->obj_total += gnc_book_count_transactions( book );
     be->operations_done = 0;
 
-    (void)gnc_sql_connection_begin_transaction( be->conn );
+    is_ok = gnc_sql_connection_begin_transaction( be->conn );
 
     // FIXME: should write the set of commodities that are used
     //write_commodities( be, book );
-    is_ok = gnc_sql_save_book( be, QOF_INSTANCE(book) );
     if ( is_ok )
     {
+        is_ok = gnc_sql_save_book( be, QOF_INSTANCE(book) );
+    }
+    if ( is_ok )
+    {
         is_ok = write_accounts( be );
     }
     if ( is_ok )
@@ -519,9 +522,12 @@
     {
         qof_object_foreach_backend( GNC_SQL_BACKEND, write_cb, be );
     }
-    if ( is_ok ) 
+    if ( is_ok )
     {
-        (void)gnc_sql_connection_commit_transaction( be->conn );
+        is_ok = gnc_sql_connection_commit_transaction( be->conn );
+    }
+    if ( is_ok )
+    {
         be->is_pristine_db = FALSE;
 
         // Mark the book as clean
@@ -529,7 +535,7 @@
     }
     else
     {
-        gnc_sql_connection_rollback_transaction( be->conn );
+        is_ok = gnc_sql_connection_rollback_transaction( be->conn );
     }
     LEAVE( "book=%p", book );
 }
@@ -623,7 +629,12 @@
         return;
     }
 
-    (void)gnc_sql_connection_begin_transaction( be->conn );
+    if ( !gnc_sql_connection_begin_transaction( be->conn ) )
+    {
+        PERR( "gnc_sql_commit_edit(): begin_transaction failed\n" );
+        LEAVE( "Rolled back - database transaction begin error" );
+        return;
+    }
 
     be_data.is_known = FALSE;
     be_data.be = be;

Modified: gnucash/trunk/src/engine/Split.c
===================================================================
--- gnucash/trunk/src/engine/Split.c	2010-10-29 08:32:07 UTC (rev 19727)
+++ gnucash/trunk/src/engine/Split.c	2010-10-29 08:39:29 UTC (rev 19728)
@@ -778,8 +778,9 @@
        original and new transactions, for the _next_ begin/commit cycle. */
     s->orig_acc = s->acc;
     s->orig_parent = s->parent;
-    qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, NULL,
-                          (void (*) (QofInstance *)) xaccFreeSplit);
+    if (!qof_commit_edit_part2(QOF_INSTANCE(s), commit_err, NULL,
+                               (void (*) (QofInstance *)) xaccFreeSplit))
+        return;
 
     if (acc)
     {



More information about the gnucash-changes mailing list