clean up error code handling in SQL backend

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4142 57a11ea4-9604-0410-9ed3-97b8803252fd
zzzoldreleases/1.6
Linas Vepstas 25 years ago
parent 92e202d05f
commit bab13c88e5

@ -67,14 +67,48 @@ show_book_error (GNCBackendError io_error, const char *newfile)
case ERR_BACKEND_NO_BACKEND:
fmt = _("The URL \n %s\n"
"is not supported by this version of GnuCash.");
"is not supported by this version of GnuCash.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_BAD_URL:
fmt = _("Can't parse the URL\n %s\n");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_CANT_CONNECT:
fmt = _("Can't connect to\n %s\n"
"The host, username or password were incorrect.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_CONN_LOST:
fmt = _("Can't connect to\n %s\n"
"Connection was lost, unable to send data.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_LOCKED:
fmt = _("The URL \n %s\n"
"is in use by another user.");
"is in use by another user.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_DATA_CORRUPT:
fmt = _("The URL \n %s\n"
"does not contain GnuCash data or the data is corrupt.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_BACKEND_SERVER_ERR:
fmt = _("The server at URL \n %s\n"
"experienced an error or encountered bad or corrupt data.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
@ -122,19 +156,6 @@ show_book_error (GNCBackendError io_error, const char *newfile)
gnc_error_dialog(buf);
break;
case ERR_SQL_BAD_LOCATION:
fmt = _("Can't parse the database URL\n %s\n");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
case ERR_SQL_CANT_CONNECT:
fmt = _("Can't connect to the database\n %s\n"
"The host, username or password were incorrect.");
buf = g_strdup_printf (fmt, newfile);
gnc_error_dialog (buf);
break;
default:
PERR("FIXME: Unhandled error %d", io_error);
fmt = _("An unknown I/O error occurred.");

@ -44,13 +44,18 @@ typedef enum {
ERR_BACKEND_NO_ERR = 0,
ERR_BACKEND_NO_BACKEND, /* Backend * pointer was null the err routine */
/* or no backend handler (ENOSYS) */
ERR_BACKEND_LOCKED, /* in use by another user (ETXTBSY) */
ERR_BACKEND_BAD_URL, /* Can't parse url */
ERR_BACKEND_NO_SUCH_DB, /* the named database doesn't exist */
ERR_BACKEND_CANT_CONNECT, /* bad dbname/login/passwd or network failure */
ERR_BACKEND_CONN_LOST, /* Lost connection to server */
ERR_BACKEND_LOCKED, /* in use by another user (ETXTBSY) */
ERR_BACKEND_DATA_CORRUPT, /* data in db is corrupt */
ERR_BACKEND_SERVER_ERR, /* error in response from server */
ERR_BACKEND_ALLOC, /* internal memory allocation failure */
ERR_BACKEND_MISC, /* undetermined error */
/* fileio errors */
ERR_FILEIO_FILE_BAD_READ, /* read failed or file prematurely truncated */
ERR_FILEIO_FILE_BAD_READ = 1000, /* read failed or file prematurely truncated */
ERR_FILEIO_FILE_EMPTY, /* file exists, is readable, but is empty */
ERR_FILEIO_FILE_LOCKERR, /* mangled locks (unspecified error) */
ERR_FILEIO_FILE_NOT_FOUND, /* not found / no such file */
@ -59,30 +64,19 @@ typedef enum {
ERR_FILEIO_UNKNOWN_FILE_TYPE,
/* network errors */
ERR_NETIO_NO_CONNECTION, /* network failure, can't connect to server */
ERR_NETIO_SHORT_READ, /* not enough bytes received */
ERR_NETIO_SHORT_READ = 2000, /* not enough bytes received */
ERR_NETIO_WRONG_CONTENT_TYPE, /* wrong kind of server, wrong data served */
ERR_NETIO_NOT_GNCXML, /* whatever it is, we can't parse it. */
/* database errors */
ERR_SQL_BAD_LOCATION, /* can't parse url */
ERR_SQL_CANT_CONNECT, /* bad dbname/login/passwd or network failure */
ERR_SQL_SEND_QUERY_FAILED, /* can't send to database */
ERR_SQL_FINISH_QUERY_FAILED, /* can't finish out sent request */
ERR_SQL_GET_RESULT_FAILED, /* can't read response from the db. */
ERR_SQL_CORRUPT_DB, /* data in db is corrupt */
ERR_SQL_MISSING_DATA, /* database doesn't contain expected data */
ERR_SQL_MISSING_DATA = 3000, /* database doesn't contain expected data */
/* RPC errors */
ERR_RPC_BAD_URL, /* Can't parse url */
ERR_RPC_HOST_UNK, /* Host unknown */
ERR_RPC_CANT_CONNECT, /* bad hostname/port/dbname/etc. */
ERR_RPC_HOST_UNK = 4000, /* Host unknown */
ERR_RPC_CANT_BIND, /* can't bind to address */
ERR_RPC_CANT_ACCEPT, /* can't accept connection */
ERR_RPC_NO_CONNECTION, /* no connection to server */
ERR_RPC_CONNECTION_LOST, /* Lost connection to server */
ERR_RPC_BAD_VERSION, /* RPC Version Mismatch */
ERR_RPC_SERVER_STATE, /* Invalid/bad server state */
ERR_RPC_FAILED, /* Operation failed */
ERR_RPC_NOT_ADDED, /* object not added */

@ -809,7 +809,7 @@ static void rpcend_book_begin (GNCBook *book, const char *book_id,
* rpc://host[:port]/db_name
*/
if (strncmp (book_id, "rpc://", 6)) {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
xaccBackendSetError (&be->be, ERR_BACKEND_BAD_URL);
LEAVE ("Not an RPC URL?");
return;
}
@ -817,7 +817,7 @@ static void rpcend_book_begin (GNCBook *book, const char *book_id,
start = url + 6;
rest = strchr (start, '/');
if (!rest || *rest == '\0') {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
xaccBackendSetError (&be->be, ERR_BACKEND_BAD_URL);
g_free (url);
LEAVE ("cannot find a path after host[:port]");
return;
@ -832,7 +832,7 @@ static void rpcend_book_begin (GNCBook *book, const char *book_id,
be->hostname = g_strdup (start);
start = rest+1;
if (*start == '\0') {
xaccBackendSetError (&be->be, ERR_RPC_BAD_URL);
xaccBackendSetError (&be->be, ERR_BACKEND_BAD_URL);
g_free (url);
LEAVE ("tailing slash but no path after host[:port]");
return;

@ -13,6 +13,8 @@
#include <netdb.h>
#include <unistd.h>
#include <rpc/xprt_thrd.h>
#include <sys/socket.h>
#include "RpcBackend.h"
#include "RpcSock.h"
@ -103,7 +105,7 @@ int RpcConnect (char *hostname, unsigned short port, RPCSock **sock)
struct sockaddr_in sin;
if (hostname == NULL || *hostname == '\0' || port == 0)
return ERR_RPC_BAD_URL;
return ERR_BACKEND_BAD_URL;
if (sock == NULL)
return ERR_BACKEND_MISC;
@ -118,8 +120,8 @@ int RpcConnect (char *hostname, unsigned short port, RPCSock **sock)
if ((s = socket (sin.sin_family, SOCK_STREAM, 0)) < 0)
return ERR_BACKEND_ALLOC;
if (connect (s, &sin, sizeof(sin)) != 0)
return ERR_RPC_CANT_CONNECT;
if (connect (s, (struct sockaddr *) &sin, sizeof(sin)) != 0)
return ERR_BACKEND_CANT_CONNECT;
new = g_malloc (sizeof (*new));
if (new == NULL) {
@ -186,7 +188,7 @@ int RpcCreateListener (unsigned short port, RPCSock **sock)
sin.sin_family = AF_INET;
sin.sin_port = port;
sin.sin_addr.s_addr = INADDR_ANY;
if (bind (s, &sin, sizeof (sin)) < 0) {
if (bind (s, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
close (s);
return ERR_RPC_CANT_BIND;
}

@ -57,7 +57,7 @@ gncrpc_book_begin_1_svc(gncrpc_book_begin_args *argp, int *result, struct svc_re
{
bool_t retval = TRUE;
GncRpcSvc *cl;
int res = ERR_RPC_SERVER_STATE;
int res = ERR_BACKEND_SERVER_ERR;
ENTER ("id=\"%s\"", argp->book_id ? argp->book_id : "");
@ -91,7 +91,7 @@ gncrpc_book_load_1_svc(char *argp, gncrpc_book_load_ret *result, struct svc_req
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
result->error = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -121,7 +121,7 @@ gncrpc_book_end_1_svc(char *argp, int *result, struct svc_req *rqstp)
ENTER ("ok");
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
*result = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -168,7 +168,7 @@ gncrpc_account_commit_edit_1_svc(gncrpc_commit_acct_args *argp, int *result, str
ENTER ("vers=%d", argp->acct.vers);
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
*result = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -217,7 +217,7 @@ gncrpc_account_rollback_edit_1_svc(gncrpc_backend_guid *argp, int *result, struc
ENTER ("ok");
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
*result = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -273,7 +273,7 @@ gncrpc_txn_commit_edit_1_svc(gncrpc_commit_txn_args *argp, int *result, struct s
ENTER ("vers=%d", argp->new.vers);
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
*result = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -334,7 +334,7 @@ gncrpc_run_query_1_svc(gncrpc_query_args *argp, gncrpc_query_ret *result, struct
/* Find state */
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
result->error = ERR_BACKEND_SERVER_ERR;
LEAVE ("Bad state");
return retval;
}
@ -386,7 +386,7 @@ gncrpc_sync1_1_svc(gncrpc_sync1_args *argp, gncrpc_sync1_ret *result, struct svc
memset (result, 0, sizeof (*result));
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
result->error = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -507,7 +507,7 @@ gncrpc_sync2_1_svc(gncrpc_sync2_args *argp, int *result, struct svc_req *rqstp)
*result = 0;
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
*result = ERR_RPC_SERVER_STATE;
*result = ERR_BACKEND_SERVER_ERR;
LEAVE ("bad state");
return retval;
}
@ -543,7 +543,7 @@ gncrpc_get_txns_1_svc(gncrpc_get_txns_args *argp, gncrpc_get_txns_ret *result, s
memset (result, 0, sizeof (*result));
retval = gncrpc_get_state (rqstp, NULL, &cl);
if (!retval) {
result->error = ERR_RPC_SERVER_STATE;
result->error = ERR_BACKEND_SERVER_ERR;
LEAVE ("Bad state");
return retval;
}

@ -404,7 +404,7 @@ pgendGetAllCommodities (PGBackend *be)
char * p;
if (!be) return;
ENTER ("be=%p", be);
ENTER ("be=%p, conn=%p", be, be->connection);
comtab = gnc_engine_commodities();
if (!comtab) {
@ -857,7 +857,7 @@ pgendCopyTransactionToEngine (PGBackend *be, GUID *trans_guid)
"too many transactions with GUID=%s\n",
guid_to_string (trans_guid));
if (jrows != nrows) xaccTransCommitEdit (trans);
xaccBackendSetError (&be->be, ERR_SQL_CORRUPT_DB);
xaccBackendSetError (&be->be, ERR_BACKEND_DATA_CORRUPT);
pgendEnable(be);
gnc_engine_resume_events();
return 0;
@ -1693,11 +1693,22 @@ pgendSessionCanStart (PGBackend *be, int break_lock)
/* ============================================================= */
/* Determine whether a valid session could be obtained.
* Return TRUE if we have a session
/* The pgendSessionValidate() routine determines whether a valid
* session could be obtained.
* Return TRUE if we have a session.
* This routine is implemented attomically as a test-n-set.
*/
static gpointer
is_gnucash_cb (PGBackend *be, PGresult *result, int j, gpointer data)
{
if (TRUE == (gboolean) data) return (gpointer) TRUE;
if (0 == strcmp ("gncsession", (DB_GET_VAL ("tablename", j))))
return (gpointer) TRUE;
return FALSE;
}
static gboolean
pgendSessionValidate (PGBackend *be, int break_lock)
{
@ -1707,6 +1718,16 @@ pgendSessionValidate (PGBackend *be, int break_lock)
if (MODE_NONE == be->session_mode) return FALSE;
/* check to see if this database actually contains
* GnuCash data... */
p = "SELECT * FROM pg_tables; ";
SEND_QUERY (be,p, FALSE);
retval = (gboolean) pgendGetResults (be, is_gnucash_cb, (gpointer) FALSE);
if (FALSE == retval) {
xaccBackendSetError (&be->be, ERR_BACKEND_DATA_CORRUPT);
return FALSE;
}
/* Lock it up so that we test-n-set atomically
* i.e. we want to avoid a race condition when testing
* for the single-user session.
@ -1897,7 +1918,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid,
if (strncmp (sessionid, "postgres://", 11))
{
xaccBackendSetError (&be->be, ERR_SQL_BAD_LOCATION);
xaccBackendSetError (&be->be, ERR_BACKEND_BAD_URL);
return;
}
url = g_strdup(sessionid);
@ -1924,7 +1945,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid,
start = end+1;
if (0x0 == *start)
{
xaccBackendSetError (&be->be, ERR_SQL_BAD_LOCATION);
xaccBackendSetError (&be->be, ERR_BACKEND_BAD_URL);
g_free(url);
return;
}
@ -2062,7 +2083,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid,
}
else
{
xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT);
xaccBackendSetError (&be->be, ERR_BACKEND_CANT_CONNECT);
return;
}
}
@ -2086,7 +2107,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid,
PQerrorMessage(be->connection));
PQfinish (be->connection);
be->connection = NULL;
xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT);
xaccBackendSetError (&be->be, ERR_BACKEND_CANT_CONNECT);
return;
}
@ -2117,7 +2138,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid,
PQerrorMessage(be->connection));
PQfinish (be->connection);
be->connection = NULL;
xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT);
xaccBackendSetError (&be->be, ERR_BACKEND_CANT_CONNECT);
return;
}

@ -12,11 +12,16 @@ Postgres Install Instructions
1) Install postgresql server, client and devel packages.
2) if installed from redhat, then running /etc/rc.d/init.d/postgresql
will setup and initialize basic Postgres first-time setup & config.
3) as root, su - postgres then run 'createuser' to add your user id
4) (don't set a password on your postgres db name, yet, gnucash doesn't
have a GUI to ask for your password yet)
5) (optional) enable TCPIP connections to remote hosts. To do this:
3) as root, 'su - postgres' then run 'createuser' to add your user id
(don't set a password on your postgres db name, yet, gnucash doesn't
have a GUI to ask for your password yet)
If you've forgotten what users are on the system, you can check
by starting the 'psql' command shell and typing the command.
'SELECT * FROM pg_shadow;'
Note this wnly works for the posgres user, and other users that
have creatuser permissions.
4) (optional) enable TCPIP connections to remote hosts. To do this:
edit the file pg_hba.conf on the remote host to configure and allow
access from other hosts. See 'man pg_hba.conf' for details.
RedHat: /var/lib/pgsql/pg_hba.conf
@ -26,9 +31,17 @@ Postgres Install Instructions
Note also the user must have 'createuser' permissions in order to
lock tables (this is a bug in postgres 6.5 and maybe later ???)
5) Hints and Tips:
If you've forgotten what databases you've created in the past,
you can look the filesystem: 'ls -la /var/lib/postgres/data/base'
to view the existing databases. Alternately, if you connect as
user postgres, you can 'SELECT * FROM pg_database;'
Alternatively, you can install into a brand new database without
using root privileges. After step 1 above, perform the following:
1) Install postgresql server, client and devel packages.
2) initdb -D ~/gnucash
This creates a directory structure and supporting files
under ~/gnucash. The gnucash directory is automatically
@ -54,6 +67,7 @@ using root privileges. After step 1 above, perform the following:
connections from processes on the local host.
GnuCash Build Instructions
--------------------------
Same as usual, but you must specify the flag '--enable-sql' in order
@ -76,6 +90,9 @@ in the file open dialogs. Or try it on the command line:
/usr/local/bin/gnucash postgres://localhost/dbname_whatever
Note: GnuCash will automatically create the database if it does
not already exist. Do *not* try to create the database by hand,
or to specify a database that wasn't created by GnuCash.
Remote Access
-------------
@ -125,8 +142,6 @@ Core bugs/features that still need work:
-- distinguish between 'save' and 'save-as' in gnc-book & backend.
-- document the need for the 'gnucash' database & what its role is.
-- single-update mode is asking to 'save' data at end of session
fix this (again, ...)
@ -141,6 +156,7 @@ Core bugs/features that still need work:
GUI dialogs. This is because the backend needs to return things
like usernames, etc. in the dialogs, and the backend doesn't
have the interfaces for passing this kind of info.
(actually, the PERR/PINFO strings might do ??)
-- fix the annoying postgres:,,localhost,asdf file syntax: needs
mods to gnc-book to keep it happy about lock files & such.
@ -152,9 +168,9 @@ Core bugs/features that still need work:
there will be other debilitating conditions if the backend disappears,
leaving the engine in a possibly confused state.
for example, if postgres user is misconfigured, the LOCK TABLE
For example, if postgres user is misconfigured, the LOCK TABLE
will fail on session validate, and FINISH_QUERY will close the
connection. subsequent sql will core dump gnucash, even though this
connection. Subsequent sql will core dump gnucash, even though this
is a sysadmin error.
-- during sync, detect and report conflicting edits to accounts
@ -214,7 +230,7 @@ This list only affects the multi-user and advanced/optional features.
I don't currently have an opinion on the 'best' way of dealing with
this situation.
-- implement account rollback (i.e. of other user has modified the
-- implement account rollback (i.e. if other user has modified the
account, we need to do something to merge their work into ours...)
-- transaction rollback is 'incorrect'; sort of ?? since we should
@ -234,9 +250,10 @@ This list only affects the multi-user and advanced/optional features.
note, however, that the file format needs to save the version number
...
-- Implement logging history in the SQL server. i.e. save the old
copies of stuff in log tables. Make the username part of the
logging scheme.
-- Implement logging history/audit-trail in the SQL server.
i.e. save the old copies of stuff in log tables. Make the username
part of the logging scheme. Having 'audit trails' is considered
to be an important accounting feature.
-- let all attached client receive update events via SQL LISTEN/NOTIFY
events.

@ -75,9 +75,11 @@ If you want incremental deletion, then use the 'Single Update' mode.
Connecting to Postgres
---------
The postgres API requires a database to connect to. The initial
connect is made using the "template1" database.
----------------------
The postgres API requires a database to connect to. The initial
connect is made using the "template1" database, which is the default
database that is always created when Postgres is installed. Thus,
we assume its always present.
m4 macros
@ -132,3 +134,10 @@ are not closely synchronized. (e.g. which may happen if the machines
are not using NTP for time synchronization; or, e.g. if one machine failed
to have daylight-savings time set correctly: its transactions would be
an hour newer/older than the others, leading to bad updates).
Balances
--------
UPDATE tx
SET (balance )= (SELECT expr from .... WHERE )
WHERE txdate between today - 7 and today

@ -58,6 +58,7 @@ gpointer pgendGetResults (PGBackend *be,
#define SEND_QUERY(be,buff,retval) \
{ \
int rc; \
if (NULL == be->connection) return retval; \
rc = PQsendQuery (be->connection, buff); \
if (!rc) \
{ \
@ -65,7 +66,8 @@ gpointer pgendGetResults (PGBackend *be,
PERR("send query failed:\n" \
"\t%s", PQerrorMessage(be->connection)); \
PQfinish (be->connection); \
xaccBackendSetError (&be->be, ERR_SQL_SEND_QUERY_FAILED); \
be->connection = NULL; \
xaccBackendSetError (&be->be, ERR_BACKEND_CONN_LOST); \
return retval; \
} \
}
@ -93,7 +95,9 @@ gpointer pgendGetResults (PGBackend *be,
"\t%s", PQerrorMessage((conn))); \
PQclear(result); \
PQfinish ((conn)); \
xaccBackendSetError (&be->be, ERR_SQL_FINISH_QUERY_FAILED); \
be->connection = NULL; \
xaccBackendSetError (&be->be, ERR_BACKEND_CONN_LOST); \
break; \
} \
PQclear(result); \
i++; \
@ -118,7 +122,8 @@ gpointer pgendGetResults (PGBackend *be,
"\t%s", PQerrorMessage((conn))); \
PQclear (result); \
PQfinish (conn); \
xaccBackendSetError (&be->be, ERR_SQL_GET_RESULT_FAILED); \
be->connection = NULL; \
xaccBackendSetError (&be->be, ERR_BACKEND_SERVER_ERR); \
break; \
} \
}
@ -138,7 +143,7 @@ gpointer pgendGetResults (PGBackend *be,
} \
if (1 < nrows) { \
PERR ("unexpected duplicate records"); \
xaccBackendSetError (&be->be, ERR_SQL_CORRUPT_DB); \
xaccBackendSetError (&be->be, ERR_BACKEND_DATA_CORRUPT); \
break; \
} else if (1 == nrows)

Loading…
Cancel
Save