diff --git a/src/engine/sql/Makefile.am b/src/engine/sql/Makefile.am index 477025406b..1bf056a424 100644 --- a/src/engine/sql/Makefile.am +++ b/src/engine/sql/Makefile.am @@ -11,11 +11,13 @@ libgnc_postgres_la_SOURCES = \ EXTRA_DIST = \ builder.h \ + design.txt \ PostgresBackend.h \ README \ - demo.c \ - gnc-init.sh \ - gnc-init.sql + table.m4 \ + table-create.sql \ + table-drop.sql \ + demo.c INCLUDES = -I.. -I/usr/lib/glib/include @@ -23,8 +25,18 @@ LDFLAGS= -lpq # Some of the required C files are built with the m4 pre-processor -PostgresBackend.o: autogen.c -PostgresBackend.lo: autogen.c +PostgresBackend.o: autogen.c table-create.c table-drop.c +PostgresBackend.lo: autogen.c table-create.c table-drop.c autogen.c: table.m4 m4 table.m4 > autogen.c + +table-drop.c: table-drop.sql + echo \" > table-drop.c + cat table-drop.sql >> table-drop.c + echo \" >> table-drop.c + +table-create.c: table-create.sql + echo \" > table-create.c + cat table-create.sql >> table-create.c + echo \" >> table-create.c diff --git a/src/engine/sql/PostgresBackend.c b/src/engine/sql/PostgresBackend.c index 966a3a1e36..0e9b4d940c 100644 --- a/src/engine/sql/PostgresBackend.c +++ b/src/engine/sql/PostgresBackend.c @@ -314,6 +314,14 @@ gnc_string_to_commodity (const char *str) #include "autogen.c" +static const char *table_create_str = +#include "table-create.c" +; + +static const char *table_drop_str = +#include "table-drop.c" +; + /* ============================================================= */ /* This routine updates the account structure if needed, and/or * stores it the first time if it hasn't yet been stored. @@ -1969,12 +1977,14 @@ pgend_book_load_single (Backend *bend) /* ============================================================= */ static void -pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) +pgend_session_begin (GNCBook *sess, const char * sessionid, + gboolean ignore_lock, gboolean create_new_db) { + int really_do_create = 0; int rc; PGBackend *be; char *url, *start, *end; - char * bufp; + char *bufp; if (!sess) return; be = (PGBackend *) xaccGNCBookGetBackend (sess); @@ -1998,7 +2008,11 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) * */ - if (strncmp (sessionid, "postgres://", 11)) return; + if (strncmp (sessionid, "postgres://", 11)) + { + xaccBackendSetError (&be->be, ERR_SQL_BAD_LOCATION); + return; + } url = g_strdup(sessionid); start = url + 11; end = strchr (start, ':'); @@ -2021,7 +2035,12 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) be->hostname = g_strdup (start); } start = end+1; - if (0x0 == *start) { g_free(url); return; } + if (0x0 == *start) + { + xaccBackendSetError (&be->be, ERR_SQL_BAD_LOCATION); + g_free(url); + return; + } /* chop of trailing url-encoded junk, if present */ end = strchr (start, '?'); @@ -2053,8 +2072,88 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) be->dbName, PQerrorMessage(be->connection)); PQfinish (be->connection); be->connection = NULL; - xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT); - return; + + /* OK, this part is convoluted. + * I wish that postgres returned usable error codes. + * Alas, it does not, so we guess the true error. + * If the host is 'localhost', and we couldn't connect, + * then we assume that its because the database doesn't + * exist (although this might also happen if the database + * existed, but the user supplied a bad username/password) + */ + if (NULL == be->hostname) + { + if (create_new_db) { + really_do_create = TRUE; + } else { + xaccBackendSetError (&be->be, ERR_BACKEND_NO_SUCH_DB); + return; + } + } + else + { + xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT); + return; + } + } + + if (really_do_create) + { + char * p; + be->connection = PQsetdbLogin (be->hostname, + be->portno, + NULL, /* trace/debug options */ + NULL, /* file or tty for debug output */ + "gnucash", + NULL, /* login */ + NULL); /* pwd */ + + /* check the connection status */ + if (CONNECTION_BAD == PQstatus(be->connection)) + { + PERR("Can't connect to database 'gnucash':\n" + "\t%s", + PQerrorMessage(be->connection)); + PQfinish (be->connection); + be->connection = NULL; + xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT); + return; + } + + /* create the database */ + p = be->buff; *p =0; + p = stpcpy (p, "CREATE DATABASE "); + p = stpcpy (p, be->dbName); + p = stpcpy (p, ";"); + SEND_QUERY (be,be->buff, ); + FINISH_QUERY(be->connection); + PQfinish (be->connection); + + /* now connect to the newly created database */ + be->connection = PQsetdbLogin (be->hostname, + be->portno, + NULL, /* trace/debug options */ + NULL, /* file or tty for debug output */ + be->dbName, + NULL, /* login */ + NULL); /* pwd */ + + /* check the connection status */ + if (CONNECTION_BAD == PQstatus(be->connection)) + { + PERR("Can't connect to the newly created database '%s':\n" + "\t%s", + be->dbName, + PQerrorMessage(be->connection)); + PQfinish (be->connection); + be->connection = NULL; + xaccBackendSetError (&be->be, ERR_SQL_CANT_CONNECT); + return; + } + + /* finally, create all the tables and indexes */ + SEND_QUERY (be,table_create_str, ); + FINISH_QUERY(be->connection); } // DEBUGCMD (PQtrace(be->connection, stderr)); @@ -2088,7 +2187,8 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) be->be.trans_rollback_edit = NULL; be->be.run_query = NULL; be->be.sync = pgendSyncSingleFile; - PWARN ("MODE_SINGLE_FILE is experimental"); + PWARN ("MODE_SINGLE_FILE is beta -- we've fixed all known \n" + "bugs but that doesn't mean there aren't any!\n"); break; case MODE_SINGLE_UPDATE: @@ -2101,7 +2201,8 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) be->be.trans_rollback_edit = NULL; be->be.run_query = NULL; be->be.sync = pgendSync; - PWARN ("MODE_SINGLE_UPDATE is experimental"); + PWARN ("MODE_SINGLE_UPDATE is beta -- we've fixed all known \n" + "bugs but that doesn't mean there aren't any!\n"); break; case MODE_POLL: @@ -2114,7 +2215,7 @@ pgend_session_begin (GNCBook *sess, const char * sessionid, int ignore_lock) be->be.trans_rollback_edit = NULL; be->be.run_query = pgendRunQuery; be->be.sync = pgendSync; - PWARN ("MODE_EVENT is experimental"); + PWARN ("MODE_POLL is experimental -- you will corrupt your data\n"); break; case MODE_EVENT: diff --git a/src/engine/sql/README b/src/engine/sql/README index d9bde1528b..0ba19725eb 100644 --- a/src/engine/sql/README +++ b/src/engine/sql/README @@ -1,54 +1,67 @@ -This directory contains experimental code for sql/postgres -support. The code 'functions' and does many things but is still -missing an assortment of features, and may be buggy and crash a lot. +This directory contains code for SQL/postgres support. The +SQL backend can be used in several modes. The single-user modes +are more-or-less beta: they work, but haven't been well tested. +The multi-user code is experimental: it still has a variety of +problems. + 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 all first-time setup & config. + will setup and initialize basic postgres first-time setup & config. 3) as root, su - postgres then run 'createuser' to add your user id - -Gnucash-specific install steps ------------------------------- -A) install the m4 macro processor if you don't already have it. -B) run the script gnc-init.sh - This script will automatically create the needed tables +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) as yourself (i.e. your unix login), run 'createdb gnucash' + -To Bo Done +To Be Done ---------- --- bug: transaction memo in double line mode is lost. --- create database if it don't exist; provide user dialog for this. +Core bugs/features that still need work: --- allow user to enter url in gui dialog --- handle kvp frames --- implement account commit edit (actually, the check&rollback part) --- fix excessive use of account commit by engine --- provide support for more query types in gncquery.c --- optimize for quantity of sql traffic -- there's a lot of it, - much of it proably un-needed. - --- Implement account and transaction deletion. Deleting an account or +-- bug: Implement account and transaction deletion. Deleting an account or transaction doesn't remove it from the database. (done with split delete) --- Implement logging history in the sql server. i.e. save the old +-- bug: transaction memo in double line mode is lost. + +-- bug/feature: the 'save as' semantics are ... unexpected, if + the database already exists and has data in it ... + +-- bug: group sync doesn't pull in newer data from the db ... + +-- allow user to enter URL in GUI dialog + +-- Implement GUI to ask user for username/password to log onto the + server. + +-- handle kvp frames + +To Be Done, Part II +------------------- +This list only affects the multi-user and advanced/optional features. + +-- implement account commit edit (actually, the check&rollback part) +-- fix excessive use of account commit by engine +-- provide support for more query types in gncquery.c +-- optimize for quantity of SQL traffic -- there's a lot of it, + much of it probably un-needed. + +-- 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 gui to ask user for username/password to log onto the - server. - --- let all attached client receive update events via sql LISTEN/NOTIFY +-- let all attached client receive update events via SQL LISTEN/NOTIFY events. -- Implement various advanced database features, such as checking the user's permission to view/edit account by account ... (hmmm this - done by the dbadmin... using sql commands... which means if user + done by the dbadmin... using SQL commands... which means if user tries to write to something they're not allowed to write to, then they should be bounced back. @@ -56,32 +69,33 @@ To Bo Done should be like CVS: multiple nearly-simultaneous writers are allowed; however, only one wins, and others are kicked back. The losers know themselves because they are trying to update info of the wrong - version. But right now, version is not done everyehwere, nor is it - done uniformely: + version. But right now, version is not done everywhere, nor is it + done uniformly: -- pgend_transaction_commit does it correctly. -- pgTransactionSync does not, it clobbers. -- pgend_account_commit clobbers. -- pgendAccountGroupSync is unfinished. I'm not sure how critical this all is; with a small number of users - it shouldn'tbe a prblem. With a huge number of users, each editing + it shouldn't be a problem. With a huge number of users, each editing the same transaction (unlikely!?) then there is risk of clobbered data, but so what? --- fix rollback bug in gui. If backend tells engine to rollback, - (e.g. on a new transaction), the gui still shows traces, instead +-- fix rollback bug in GUI. If backend tells engine to rollback, + (e.g. on a new transaction), the GUI still shows traces, instead of kicking back. - This is a symptom of a lack of kick-back detection in gui. + This is a symptom of a lack of kick-back detection in GUI. -- finish implementing pgendAccountGroupSync -- store account balances in database. This will be tricky ... -- split query gets hard ... + -- use aggregates ?? -- review multiuser operation for correctness -- fix caching in the face of lost contact to the backend. If the backend can't contact its server, then we should just save up caches, and then when contact with backend re-established, we should spit - them out. But right now, this is brroken. In particular, + them out. But right now, this is broken. In particular, the use of xaccGrouparkSaved screws up some status bits ... -- review & match up against docs at