From 07fb44968ddb16b51f7e844e6e4dbe0758d08d97 Mon Sep 17 00:00:00 2001 From: Linas Vepstas Date: Sun, 10 Jun 2001 15:38:20 +0000 Subject: [PATCH] split file into two git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4583 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/engine/sql/Makefile.am | 4 +- src/engine/sql/PostgresBackend.c | 222 +----------------------- src/engine/sql/README | 7 + src/engine/sql/events.c | 281 +++++++++++++++++++++++++++++++ src/engine/sql/events.h | 47 ++++++ 5 files changed, 339 insertions(+), 222 deletions(-) create mode 100644 src/engine/sql/events.c create mode 100644 src/engine/sql/events.h diff --git a/src/engine/sql/Makefile.am b/src/engine/sql/Makefile.am index f2db553b33..db3f273042 100644 --- a/src/engine/sql/Makefile.am +++ b/src/engine/sql/Makefile.am @@ -2,7 +2,7 @@ # Build the postgres backend as its own loadable shared object. lib_LTLIBRARIES = libgnc_postgres.la -libgnc_postgres_la_LDFLAGS = -version-info 6:3:6 +libgnc_postgres_la_LDFLAGS = -version-info 7:0:7 libgnc_postgres_la_SOURCES = \ @@ -10,6 +10,7 @@ libgnc_postgres_la_SOURCES = \ builder.c \ checkpoint.c \ escape.c \ + events.c \ gncquery.c \ kvp-sql.c @@ -18,6 +19,7 @@ noinst_HEADERS = \ builder.h \ checkpoint.h \ escape.h \ + events.h \ gncquery.h \ kvp-sql.h \ putil.h diff --git a/src/engine/sql/PostgresBackend.c b/src/engine/sql/PostgresBackend.c index 201a943a67..7371df6509 100644 --- a/src/engine/sql/PostgresBackend.c +++ b/src/engine/sql/PostgresBackend.c @@ -26,8 +26,6 @@ * * FUNCTION: * Implements the callbacks for the Postgres backend. - * The SINGLE modes should work and are more-or-less feature complete. - * The multi-user modes are mostly implemented, and possibly useful. * * HISTORY: * Copyright (c) 2000, 2001 Linas Vepstas @@ -69,6 +67,7 @@ #include "builder.h" #include "checkpoint.h" +#include "events.h" #include "gncquery.h" #include "kvp-sql.h" #include "PostgresBackend.h" @@ -234,179 +233,6 @@ static const char *table_drop_str = #include "table-drop.c" ; -/* ============================================================= */ -/* ============================================================= */ -/* EVENT NOTIFICATION HANDLER */ -/* ============================================================= */ -/* ============================================================= */ - -static gboolean -pgendEventsPending (Backend *bend) -{ - PGBackend *be = (PGBackend *) bend; - PGnotify *note; - char *p; - int rc; - - if (!be) return FALSE; - ENTER ("mypid=%d", be->my_pid); - - /* No need to handle events in single-modes */ - if ((MODE_SINGLE_UPDATE == be->session_mode) || - (MODE_SINGLE_FILE == be->session_mode)) - { - return FALSE; - } - - /* consolidate multiple event notifications */ - rc = PQconsumeInput (be->connection); - if (1 != rc) - { - PERR ("consume input failed: %s", PQerrorMessage(be->connection)); - } - - note = PQnotifies (be->connection); - while (note) - { - /* ignore notifies from myself */ - if (note->be_pid == be->my_pid) - { - PINFO ("this event from myself: %s from pid=%d", note->relname, note->be_pid); - free (note); - note = PQnotifies (be->connection); - continue; - } - - PINFO ("notify event %s from pid=%d", note->relname, note->be_pid); - - if (0 == strcasecmp ("gncTransaction", note->relname)) - { - be->do_transaction ++; - } - else - if (0 == strcasecmp ("gncCheckpoint", note->relname)) - { - be->do_checkpoint ++; - } - else - if (0 == strcasecmp ("gncPrice", note->relname)) - { - be->do_price ++; - } - else - if (0 == strcasecmp ("gncAccount", note->relname)) - { - be->do_account ++; - } - else - if (0 == strcasecmp ("gncSession", note->relname)) - { - be->do_session ++; - } - else - { - PERR ("unexpected notify %s", note->relname) - } - - /* get the next one */ - free (note); - note = PQnotifies (be->connection); - } - - /* for now, we ignore session and checkpoint events */ - if (be->do_transaction + be->do_price + be->do_account) return TRUE; - return FALSE; -} - -static gpointer -get_event_cb (PGBackend *be, PGresult *result, int j, gpointer data) -{ - GNCEngineEventType type; - GUID guid; - Timespec ts; - Timespec *latest = (Timespec *) data; - char change = (DB_GET_VAL("change",j))[0]; - - string_to_guid (DB_GET_VAL("guid",j), &guid); - ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_changed",j)); - - if (0 < timespec_cmp(&ts, latest)) *latest = ts; - - switch (change) - { - case 'a': type = GNC_EVENT_CREATE; break; - case 'm': type = GNC_EVENT_MODIFY; break; - case 'd': type = GNC_EVENT_DESTROY; break; - default: - PERR ("unknown change type %c", change); - return data; - } - - PINFO ("event %c for %s", change, DB_GET_VAL("guid",j)); - gnc_engine_generate_event (&guid, type); - - return data; -} - -#define GET_EVENTS(guid_name,table,timestamp) \ -{ \ - Timespec latest; \ - char *p; \ - latest.tv_sec = -2; \ - latest.tv_nsec = 0; \ - \ - p = be->buff; *p = 0; \ - p = stpcpy (p, "SELECT change, date_changed, " #guid_name \ - " AS guid FROM " #table \ - " WHERE sessionGuid <> '"); \ - p = stpcpy (p, be->session_guid_str); \ - p = stpcpy (p, "' AND date_changed >= '"); \ - p = gnc_timespec_to_iso8601_buff (timestamp, p); \ - p = stpcpy (p, "';"); \ - \ - SEND_QUERY (be, be->buff, FALSE); \ - pgendGetResults (be, get_event_cb, &latest); \ - \ - if (0 < timespec_cmp(&latest, &(timestamp))) \ - { \ - (timestamp) = latest; \ - } \ -} - -static gboolean -pgendProcessEvents (Backend *bend) -{ - PGBackend *be = (PGBackend *) bend; - - if (!be) return FALSE; - - ENTER (" "); - - /* handle each event type */ - if (be->do_account) - { - GET_EVENTS (accountGuid, gncAccountTrail, be->last_account); - } - if (be->do_price) - { - GET_EVENTS (priceGuid, gncPriceTrail, be->last_price); - } - if (be->do_transaction) - { - GET_EVENTS (transGuid, gncTransactionTrail, be->last_transaction); - - /* gnc_cm_event_handler() doesn't really want to see any split guids */ - // GET_EVENTS (entryGuid, gncEntryTrail, be->last_transaction); - } - - be->do_account = 0; - be->do_checkpoint = 0; - be->do_price = 0; - be->do_session = 0; - be->do_transaction = 0; - return FALSE; -} - /* ============================================================= */ /* ============================================================= */ @@ -2529,52 +2355,6 @@ pgendSyncPriceDBSingleFile (Backend *bend, GNCPriceDB *prdb) LEAVE(" "); } -/* ============================================================= */ -/* find the backend pid */ - -static void -pgendSessionGetPid (PGBackend *be) -{ - PGnotify *note; - char *p; - long int r; - - r = random (); - - p = be->buff; *p=0; - sprintf (p, "LISTEN \"%ld\";\n NOTIFY \"%ld\";", r, r); - SEND_QUERY (be, be->buff, ); - FINISH_QUERY(be->connection); - note = PQnotifies (be->connection); - if (!note) - { - PERR ("didn't receive notification"); - return; - } - - be->my_pid = note->be_pid; - PINFO ("postgres backend pid =%d", be->my_pid); - - p = be->buff; *p=0; - sprintf (p, "UNLISTEN \"%ld\";", r); - SEND_QUERY (be, be->buff, ); - FINISH_QUERY(be->connection); -} - -/* ============================================================= */ - -static void -pgendSessionSetupNotifies (PGBackend *be) -{ - char *p; - - p = "LISTEN gncSession;\nLISTEN gncAccount;\n" - "LISTEN gncPrice;\nLISTEN gncTransaction;\n" - "LISTEN gncCheckpoint;"; - SEND_QUERY (be, p, ); - FINISH_QUERY(be->connection); -} - /* ============================================================= */ static const char * diff --git a/src/engine/sql/README b/src/engine/sql/README index 808380246a..8db0160ce7 100644 --- a/src/engine/sql/README +++ b/src/engine/sql/README @@ -294,8 +294,15 @@ operation is probably OK. They're not needed until there are a lot of transactions in the system. Need to recompute checkpoints on some periodic basis. +-- get notify timestamp from trail. (to avoid clock skew) + -- NOTIFY is not updating main window balances ... +-- notify does not update main window or account info after an + edit. + +-- transaction deletion not handled by notify + -- during sync, detect and report conflicting edits to accounts and transactions. See the notes for pgendSync() for details as to what this is about. For the first pass, this is not a diff --git a/src/engine/sql/events.c b/src/engine/sql/events.c new file mode 100644 index 0000000000..1866e45ca0 --- /dev/null +++ b/src/engine/sql/events.c @@ -0,0 +1,281 @@ +/********************************************************************\ + * events.c -- implements event handling for 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: + * events.c + * + * FUNCTION: + * Implements the event-handling callbacks for the Postgres backend. + * + * HISTORY: + * Copyright (c) 2000, 2001 Linas Vepstas + * + */ + +#define _GNU_SOURCE + +#include "config.h" + +#include + +#include "Backend.h" +#include "BackendP.h" +#include "events.h" +#include "gnc-engine-util.h" +#include "gnc-event.h" +#include "gnc-event-p.h" +#include "guid.h" +#include "GNCId.h" +#include "GNCIdP.h" + +#include "PostgresBackend.h" + +#include "putil.h" + +static short module = MOD_BACKEND; + + +/* ============================================================= */ +/* ============================================================= */ +/* EVENT NOTIFICATION HANDLER */ +/* ============================================================= */ +/* ============================================================= */ + +gboolean +pgendEventsPending (Backend *bend) +{ + PGBackend *be = (PGBackend *) bend; + PGnotify *note; + char *p; + int rc; + + if (!be) return FALSE; + ENTER ("mypid=%d", be->my_pid); +PWARN("mypid=%d", be->my_pid); + + /* No need to handle events in single-modes */ + if ((MODE_SINGLE_UPDATE == be->session_mode) || + (MODE_SINGLE_FILE == be->session_mode)) + { + return FALSE; + } + + /* consolidate multiple event notifications */ + rc = PQconsumeInput (be->connection); + if (1 != rc) + { + PERR ("consume input failed: %s", PQerrorMessage(be->connection)); + } + + note = PQnotifies (be->connection); + while (note) + { + /* ignore notifies from myself */ + if (note->be_pid == be->my_pid) + { + PINFO ("this event from myself: %s from pid=%d", note->relname, note->be_pid); + free (note); + note = PQnotifies (be->connection); + continue; + } + + PINFO ("notify event %s from pid=%d", note->relname, note->be_pid); +PWARN ("notify event %s from pid=%d", note->relname, note->be_pid); + + if (0 == strcasecmp ("gncTransaction", note->relname)) + { + be->do_transaction ++; + } + else + if (0 == strcasecmp ("gncCheckpoint", note->relname)) + { + be->do_checkpoint ++; + } + else + if (0 == strcasecmp ("gncPrice", note->relname)) + { + be->do_price ++; + } + else + if (0 == strcasecmp ("gncAccount", note->relname)) + { + be->do_account ++; + } + else + if (0 == strcasecmp ("gncSession", note->relname)) + { + be->do_session ++; + } + else + { + PERR ("unexpected notify %s", note->relname) + } + + /* get the next one */ + free (note); + note = PQnotifies (be->connection); + } + + /* for now, we ignore session and checkpoint events */ + if (be->do_transaction + be->do_price + be->do_account) return TRUE; + return FALSE; +} + +static gpointer +get_event_cb (PGBackend *be, PGresult *result, int j, gpointer data) +{ + GNCEngineEventType type; + GUID guid; + Timespec ts; + Timespec *latest = (Timespec *) data; + char change = (DB_GET_VAL("change",j))[0]; + + string_to_guid (DB_GET_VAL("guid",j), &guid); + ts = gnc_iso8601_to_timespec_local (DB_GET_VAL("date_changed",j)); + + if (0 < timespec_cmp(&ts, latest)) *latest = ts; + + switch (change) + { + case 'a': type = GNC_EVENT_CREATE; break; + case 'm': type = GNC_EVENT_MODIFY; break; + case 'd': type = GNC_EVENT_DESTROY; break; + default: + PERR ("unknown change type %c", change); + return data; + } + + PINFO ("event %c for %s", change, DB_GET_VAL("guid",j)); +PWARN ("event %c for %s", change, DB_GET_VAL("guid",j)); + gnc_engine_generate_event (&guid, type); + + return data; +} + +#define GET_EVENTS(guid_name,table,timestamp) \ +{ \ + Timespec latest; \ + char *p; \ + latest.tv_sec = -2; \ + latest.tv_nsec = 0; \ + \ + p = be->buff; *p = 0; \ + p = stpcpy (p, "SELECT change, date_changed, " #guid_name \ + " AS guid FROM " #table \ + " WHERE sessionGuid <> '"); \ + p = stpcpy (p, be->session_guid_str); \ + p = stpcpy (p, "' AND date_changed >= '"); \ + p = gnc_timespec_to_iso8601_buff (timestamp, p); \ + p = stpcpy (p, "';"); \ + \ + SEND_QUERY (be, be->buff, FALSE); \ + pgendGetResults (be, get_event_cb, &latest); \ + \ + if (0 < timespec_cmp(&latest, &(timestamp))) \ + { \ + (timestamp) = latest; \ + } \ +} + +gboolean +pgendProcessEvents (Backend *bend) +{ + PGBackend *be = (PGBackend *) bend; + + if (!be) return FALSE; + + ENTER (" "); + + /* handle each event type */ + if (be->do_account) + { + GET_EVENTS (accountGuid, gncAccountTrail, be->last_account); + } + if (be->do_price) + { + GET_EVENTS (priceGuid, gncPriceTrail, be->last_price); + } + if (be->do_transaction) + { + GET_EVENTS (transGuid, gncTransactionTrail, be->last_transaction); + + /* gnc_cm_event_handler() doesn't really want to see any split guids */ + // GET_EVENTS (entryGuid, gncEntryTrail, be->last_transaction); + } + + be->do_account = 0; + be->do_checkpoint = 0; + be->do_price = 0; + be->do_session = 0; + be->do_transaction = 0; + return FALSE; +} + + +/* ============================================================= */ +/* find the backend pid */ + +void +pgendSessionGetPid (PGBackend *be) +{ + PGnotify *note; + char *p; + long int r; + + r = random (); + + p = be->buff; *p=0; + sprintf (p, "LISTEN \"%ld\";\n NOTIFY \"%ld\";", r, r); + SEND_QUERY (be, be->buff, ); + FINISH_QUERY(be->connection); + note = PQnotifies (be->connection); + if (!note) + { + PERR ("didn't receive notification"); + return; + } + + be->my_pid = note->be_pid; + PINFO ("postgres backend pid =%d", be->my_pid); + + p = be->buff; *p=0; + sprintf (p, "UNLISTEN \"%ld\";", r); + SEND_QUERY (be, be->buff, ); + FINISH_QUERY(be->connection); +} + +/* ============================================================= */ + +void +pgendSessionSetupNotifies (PGBackend *be) +{ + char *p; + + p = "LISTEN gncSession;\nLISTEN gncAccount;\n" + "LISTEN gncPrice;\nLISTEN gncTransaction;\n" + "LISTEN gncCheckpoint;"; + SEND_QUERY (be, p, ); + FINISH_QUERY(be->connection); +} + +/* ======================== END OF FILE ======================== */ diff --git a/src/engine/sql/events.h b/src/engine/sql/events.h new file mode 100644 index 0000000000..f1c01f77bb --- /dev/null +++ b/src/engine/sql/events.h @@ -0,0 +1,47 @@ +/********************************************************************\ + * events.h -- implements event handling for 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: + * events.h + * + * FUNCTION: + * Implements the event handling callbacks for the postgres backend. + * + * HISTORY: + * Copyright (c) 2001 Linas Vepstas + */ + + +#ifndef __POSTGRES_EVENTS_H__ +#define __POSTGRES_EVENTS_H__ + +#include "Backend.h" +#include "PostgresBackend.h" + +gboolean pgendEventsPending (Backend *); +gboolean pgendProcessEvents (Backend *); + +void pgendSessionGetPid (PGBackend *); +void pgendSessionSetupNotifies (PGBackend *); + + +#endif /* __POSTGRES_EVENTS_H__ */