* 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.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@7485 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Derek Atkins 2002-11-16 23:17:09 +00:00
parent d4f97ce2d9
commit c2bfd78517
11 changed files with 241 additions and 37 deletions

View File

@ -1,3 +1,15 @@
2002-11-16 Derek Atkins <derek@ihtfp.com>
* 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 <jsled@asynchronous.org>
* src/gnome/window-register.c (gnc_register_delete_cb): Change the

View File

@ -1,3 +1,17 @@
2002-11-16 gettextize <bug-gnu-gettext@gnu.org>
* 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 <bug-gnu-gettext@gnu.org>
* codeset.m4: New file, from gettext-0.11.1.

View File

@ -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 \

View File

@ -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";
}

View File

@ -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
{

View File

@ -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 <mevanecek@yahoo.com>
*
*/
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;
}

View File

@ -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); \

View File

@ -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;

View File

@ -22,7 +22,7 @@
\********************************************************************/
#include "config.h"
#include <stdarg.h>
#include <glib.h>
#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;

View File

@ -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

View File

@ -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)
{