diff --git a/ChangeLog b/ChangeLog index 6b6ed18eb0..0451bb0a88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2002-11-16 Derek Atkins + + * Matthew Vanecek's patch: + + The attached patch contains some immediate bug fixes for the SQL + back end, and it adds and error message string + accessor/mutator + to Backend. Also, in xaccAccountCommitEdit(), added the use of + PWARN_GUI to display the error message. + + The bug fixes in the SQL backend involved converting the *_QUERY + macros to functions. + 2002-11-15 Joshua Sled * src/gnome/window-register.c (gnc_register_delete_cb): Change the diff --git a/m4/ChangeLog b/m4/ChangeLog index 377f546afe..2be6327a8a 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,17 @@ +2002-11-16 gettextize + + * codeset.m4: New file, from gettext-0.11.1. + * gettext.m4: New file, from gettext-0.11.1. + * glibc21.m4: New file, from gettext-0.11.1. + * iconv.m4: New file, from gettext-0.11.1. + * isc-posix.m4: New file, from gettext-0.11.1. + * lcmessage.m4: New file, from gettext-0.11.1. + * lib-ld.m4: New file, from gettext-0.11.1. + * lib-link.m4: New file, from gettext-0.11.1. + * lib-prefix.m4: New file, from gettext-0.11.1. + * progtest.m4: New file, from gettext-0.11.1. + * Makefile.am (EXTRA_DIST): Add the new files. + 2002-05-13 gettextize * codeset.m4: New file, from gettext-0.11.1. diff --git a/src/backend/postgres/Makefile.am b/src/backend/postgres/Makefile.am index 0468d766a1..86758400be 100644 --- a/src/backend/postgres/Makefile.am +++ b/src/backend/postgres/Makefile.am @@ -22,7 +22,8 @@ libgncmod_backend_postgres_la_SOURCES = \ price.c \ txn.c \ txnmass.c \ - upgrade.c + upgrade.c \ + putil.c noinst_HEADERS = \ PostgresBackend.h \ diff --git a/src/backend/postgres/PostgresBackend.c b/src/backend/postgres/PostgresBackend.c index 82b8e50d6a..bde6af2171 100644 --- a/src/backend/postgres/PostgresBackend.c +++ b/src/backend/postgres/PostgresBackend.c @@ -70,10 +70,10 @@ #include "txnmass.h" #include "upgrade.h" -#include "putil.h" - static short module = MOD_BACKEND; +#include "putil.h" + static void pgendInit (PGBackend *be); static const char * pgendSessionGetMode (PGBackend *be); @@ -1127,7 +1127,6 @@ pgendSessionGetMode (PGBackend *be) return "POLL"; case MODE_EVENT: return "EVENT"; - default: } return "ERROR"; } diff --git a/src/backend/postgres/account.c b/src/backend/postgres/account.c index 44a0e2e074..aa469a50a7 100644 --- a/src/backend/postgres/account.c +++ b/src/backend/postgres/account.c @@ -49,10 +49,10 @@ #include "PostgresBackend.h" #include "price.h" -#include "putil.h" - static short module = MOD_BACKEND; +#include "putil.h" + /* ============================================================= */ /* ============================================================= */ /* ACCOUNT AND GROUP STUFF */ @@ -577,6 +577,7 @@ pgend_account_commit_edit (Backend * bend, { AccountGroup *parent; char *p; + GNCBackendError err; PGBackend *be = (PGBackend *)bend; ENTER ("be=%p, acct=%p", be, acct); @@ -624,15 +625,18 @@ pgend_account_commit_edit (Backend * bend, if (acct->do_free) { const GUID *guid = xaccAccountGetGUID(acct); - pgendStoreAuditAccount (be, acct, SQL_DELETE); pgendKVPDelete (be, acct->idata); p = be->buff; *p = 0; p = stpcpy (p, "DELETE FROM gncAccount WHERE accountGuid='"); p = guid_to_string_buff (guid, p); p = stpcpy (p, "';"); - SEND_QUERY (be,be->buff,); - FINISH_QUERY(be->connection); + err = sendQuery (be,be->buff); + if (err == ERR_BACKEND_NO_ERR) { + err = finishQuery(be); + if (err > 0) /* if the number of rows deleted is 0 */ + pgendStoreAuditAccount (be, acct, SQL_DELETE); + } } else { diff --git a/src/backend/postgres/putil.c b/src/backend/postgres/putil.c new file mode 100644 index 0000000000..5b93455e75 --- /dev/null +++ b/src/backend/postgres/putil.c @@ -0,0 +1,106 @@ +/********************************************************************\ + * putil.c -- utility macros for the postgres backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * + * Boston, MA 02111-1307, USA gnu@gnu.org * +\********************************************************************/ + +/* + * FILE: + * putil.c + * + * FUNCTION: + * Postgres backend utility macros + * + * HISTORY: + * Copyright (c) 2002 Matthew Vanecek + * + */ + +static short module = MOD_BACKEND; + +#include "putil.h" + +/* ============================================================= */ +/* The sendQuery function sends the sql statement off to the server. + * It performs a minimal check to see that the send succeeded. The + * return value indicates success or failure of the send. + */ +int sendQuery(PGBackend *be,char * buff) { + int rc = 0; + + ENTER(" "); + if (NULL == be->connection) return ERR_BACKEND_CONN_LOST; + PINFO("Connectionn is %p", be->connection); + PINFO ("sending query %s", buff); + rc = PQsendQuery (be->connection, buff); + if (!rc) { + gchar * msg = (gchar *)PQerrorMessage(be->connection); + PERR("send query failed:\n" + "\t%s", msg); + xaccBackendSetMessage(&be->be, msg); + xaccBackendSetError (&be->be, ERR_BACKEND_SERVER_ERR); + return ERR_BACKEND_SERVER_ERR; + } + LEAVE("PQsendQuery rc = %d", rc); + return ERR_BACKEND_NO_ERR; +} + +/* --------------------------------------------------------------- */ +/* The finishQuery function makes sure that the previously sent + * query completed with no errors. It assumes that the query + * does not produce any results; if it did those results are + * discarded (only error conditions are checked for). The number of + * rows affected by the query is returned. + */ + +int finishQuery(PGBackend *be) { + int i=0; + PGresult *result; + + ENTER(" "); + /* complete/commit the transaction, check the status */ + PINFO("Connection is %p", be->connection); + do { + int x; + ExecStatusType status; + result = PQgetResult(be->connection); + if (!result) { + PINFO("Result is (null)"); + break; + } + + status = PQresultStatus(result); + x = atoi(PQcmdTuples(result)); + PINFO("Result status: %s, rows affected: %d, by %s", + PQresStatus(status), x, PQcmdStatus(result)); + i += x; + + if (PGRES_COMMAND_OK != status) { + gchar * msg = (gchar *)PQerrorMessage(be->connection); + PERR("finish query failed:\n\t%s", msg); + PQclear(result); + xaccBackendSetMessage(&be->be, msg); + xaccBackendSetError (&be->be, ERR_BACKEND_SERVER_ERR); + break; + } + PQclear(result); + } while (result); + + LEAVE("%d rows affected by SQL statement", i); + return i; +} diff --git a/src/backend/postgres/putil.h b/src/backend/postgres/putil.h index 092633cf23..d92ff63cc3 100644 --- a/src/backend/postgres/putil.h +++ b/src/backend/postgres/putil.h @@ -68,6 +68,9 @@ gpointer pgendGetResults (PGBackend *be, */ gnc_commodity * gnc_string_to_commodity (const char *str, GNCBook *book); +int sendQuery(PGBackend *be,char * buff); +int finishQuery(PGBackend *be); + /* hack alert -- calling PQfinish() on error is quite harsh, since * all subsequent sql queries will fail. On the other hand, killing * anything that follows *is* a way of minimizing data corruption @@ -89,12 +92,11 @@ gnc_commodity * gnc_string_to_commodity (const char *str, GNCBook *book); rc = PQsendQuery (be->connection, buff); \ if (!rc) \ { \ + gchar * msg = (gchar *)PQerrorMessage(be->connection); \ /* hack alert -- we need kinder, gentler error handling */\ - PERR("send query failed:\n" \ - "\t%s", PQerrorMessage(be->connection)); \ - PQfinish (be->connection); \ - be->connection = NULL; \ - xaccBackendSetError (&be->be, ERR_BACKEND_CONN_LOST); \ + PERR("send query failed:\n\t%s", msg); \ + xaccBackendSetMessage (&be->be, msg); \ + xaccBackendSetError (&be->be, ERR_BACKEND_SERVER_ERR); \ return retval; \ } \ } @@ -112,6 +114,7 @@ gnc_commodity * gnc_string_to_commodity (const char *str, GNCBook *book); PGresult *result; \ /* complete/commit the transaction, check the status */ \ do { \ + gchar *msg = NULL; \ ExecStatusType status; \ result = PQgetResult((conn)); \ if (!result) break; \ @@ -121,9 +124,8 @@ gnc_commodity * gnc_string_to_commodity (const char *str, GNCBook *book); PERR("finish query failed:\n" \ "\t%s", PQerrorMessage((conn))); \ PQclear(result); \ - PQfinish ((conn)); \ - be->connection = NULL; \ - xaccBackendSetError (&be->be, ERR_BACKEND_CONN_LOST); \ + xaccBackendSetMessage (&be->be, msg); \ + xaccBackendSetError (&be->be, ERR_BACKEND_SERVER_ERR); \ break; \ } \ PQclear(result); \ diff --git a/src/engine/Account.c b/src/engine/Account.c index 05ff5c4283..2e78ff4c49 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -474,13 +474,20 @@ xaccAccountCommitEdit (Account *acc) if (ERR_BACKEND_NO_ERR != errcode) { + char * err; /* destroys must be rolled back as well ... ??? */ acc->do_free = FALSE; /* XXX hack alert FIXME implement account rollback */ PERR (" backend asked engine to rollback, but this isn't" " handled yet. Return code=%d", errcode); + err = xaccBackendGetMessage(be); + PWARN_GUI("Error occurred while saving Account:\n%d: %s", + xaccBackendGetError(be), err); + /* push error back onto the stack */ xaccBackendSetError (be, errcode); + xaccBackendSetMessage (be, err); + g_free(err); } } acc->core_dirty = FALSE; diff --git a/src/engine/Backend.c b/src/engine/Backend.c index 2bde617b30..dcdd7b83c5 100644 --- a/src/engine/Backend.c +++ b/src/engine/Backend.c @@ -22,7 +22,7 @@ \********************************************************************/ #include "config.h" - +#include #include #include "Account.h" @@ -35,6 +35,7 @@ #include "gnc-pricedb.h" #include "gnc-pricedb-p.h" #include "TransactionP.h" +#include "messages.h" /* static short module = MOD_ENGINE; */ @@ -64,6 +65,45 @@ xaccBackendGetError (Backend *be) return err; } +void +xaccBackendSetMessage (Backend *be, const char *format, ...) { + va_list args; + char * buffer; + + if (!be) return; + + va_start(args, format); + buffer = (char *)g_strdup_vprintf(format, args); + va_end(args); + + /* If there's already something here, free it */ + if (be->error_msg) g_free(be->error_msg); + + be->error_msg = buffer; +} + +/* This should always return a valid char * */ +char * +xaccBackendGetMessage (Backend *be) { + char * msg; + + if (!be) return g_strdup(_("ERR_BACKEND_NO_BACKEND")); + if (!be->error_msg) return g_strdup(_("No error message")); + + /* + * Just return the contents of the error_msg and then set it to + * NULL. This is necessary, because the Backends don't seem to + * have a destroy_backend function to take care if freeing stuff + * up. The calling function should free the copy. + * Also, this is consistent with the xaccBackendGetError() popping. + */ + + msg = be->error_msg; + be->error_msg = NULL; + return msg; +} + + /********************************************************************\ * Fetch the backend * \********************************************************************/ @@ -123,7 +163,8 @@ xaccInitBackend(Backend *be) be->process_events = NULL; be->last_err = ERR_BACKEND_NO_ERR; - + if (be->error_msg) g_free (be->error_msg); + be->error_msg = NULL; be->percentage = NULL; be->export = NULL; diff --git a/src/engine/BackendP.h b/src/engine/BackendP.h index 5ec80df89b..99f3aff861 100644 --- a/src/engine/BackendP.h +++ b/src/engine/BackendP.h @@ -257,6 +257,7 @@ struct backend_s GNCBePercentageFunc percentage; GNCBackendError last_err; + char * error_msg; /* Export should really _NOT_ be here, but is left here for now */ void (*export) (Backend *, GNCBook *); @@ -268,10 +269,18 @@ struct backend_s * * The xaccBackendGetError() routine pops an error code off the error * stack. + * + * The xaccBackendSetMessage() assigns a string to the backend error + * message. + * + * The xaccBackendGetMessage() pops the error message string from + * the Backend. This string should be freed with g_free(). */ void xaccBackendSetError (Backend *be, GNCBackendError err); GNCBackendError xaccBackendGetError (Backend *be); +void xaccBackendSetMessage(Backend *be, const char *format, ...); +char * xaccBackendGetMessage(Backend *be); /* * The xaccGetAccountBackend() subroutine will find the diff --git a/src/engine/gnc-session.c b/src/engine/gnc-session.c index c5ac958ca3..0b131a52a9 100644 --- a/src/engine/gnc-session.c +++ b/src/engine/gnc-session.c @@ -320,6 +320,31 @@ gnc_session_load_backend(GNCSession * session, char * backend_name) /* ====================================================================== */ +static void +gnc_session_destroy_backend (GNCSession *session) +{ + g_return_if_fail (session); + + if (session->backend) + { + /* clear any error message */ + char * msg = xaccBackendGetMessage (session->backend); + g_free (msg); + + /* Then destroy the backend */ + if (session->backend->destroy_backend) + { + session->backend->destroy_backend(session->backend); + } + else + { + g_free(session->backend); + } + } + + session->backend = NULL; +} + void gnc_session_begin (GNCSession *session, const char * book_id, gboolean ignore_lock, gboolean create_if_nonexistent) @@ -365,16 +390,7 @@ gnc_session_begin (GNCSession *session, const char * book_id, PINFO ("logpath=%s", session->logpath ? session->logpath : "(null)"); /* destroy the old backend */ - if (session->backend && session->backend->destroy_backend) - { - session->backend->destroy_backend(session->backend); - } - else - { - g_free(session->backend); - } - - session->backend = NULL; + gnc_session_destroy_backend(session); /* check to see if this is a type we know how to handle */ if (!g_strncasecmp(book_id, "file:", 5) || @@ -719,14 +735,7 @@ gnc_session_destroy (GNCSession *session) gnc_session_end (session); /* destroy the backend */ - if (session->backend && session->backend->destroy_backend) - { - session->backend->destroy_backend(session->backend); - } - else - { - g_free(session->backend); - } + gnc_session_destroy_backend(session); for (node=session->books; node; node=node->next) {