split file into two

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@4583 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Linas Vepstas 2001-06-10 15:38:20 +00:00
parent 146edd60ff
commit 07fb44968d
5 changed files with 339 additions and 222 deletions

View File

@ -2,7 +2,7 @@
# Build the postgres backend as its own loadable shared object. # Build the postgres backend as its own loadable shared object.
lib_LTLIBRARIES = libgnc_postgres.la 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 = \ libgnc_postgres_la_SOURCES = \
@ -10,6 +10,7 @@ libgnc_postgres_la_SOURCES = \
builder.c \ builder.c \
checkpoint.c \ checkpoint.c \
escape.c \ escape.c \
events.c \
gncquery.c \ gncquery.c \
kvp-sql.c kvp-sql.c
@ -18,6 +19,7 @@ noinst_HEADERS = \
builder.h \ builder.h \
checkpoint.h \ checkpoint.h \
escape.h \ escape.h \
events.h \
gncquery.h \ gncquery.h \
kvp-sql.h \ kvp-sql.h \
putil.h putil.h

View File

@ -26,8 +26,6 @@
* *
* FUNCTION: * FUNCTION:
* Implements the callbacks for the Postgres backend. * 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: * HISTORY:
* Copyright (c) 2000, 2001 Linas Vepstas * Copyright (c) 2000, 2001 Linas Vepstas
@ -69,6 +67,7 @@
#include "builder.h" #include "builder.h"
#include "checkpoint.h" #include "checkpoint.h"
#include "events.h"
#include "gncquery.h" #include "gncquery.h"
#include "kvp-sql.h" #include "kvp-sql.h"
#include "PostgresBackend.h" #include "PostgresBackend.h"
@ -234,179 +233,6 @@ static const char *table_drop_str =
#include "table-drop.c" #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(" "); 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 * static const char *

View File

@ -294,8 +294,15 @@ operation is probably OK.
They're not needed until there are a lot of transactions in the They're not needed until there are a lot of transactions in the
system. Need to recompute checkpoints on some periodic basis. 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 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 -- during sync, detect and report conflicting edits to accounts
and transactions. See the notes for pgendSync() for details and transactions. See the notes for pgendSync() for details
as to what this is about. For the first pass, this is not a as to what this is about. For the first pass, this is not a

281
src/engine/sql/events.c Normal file
View File

@ -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 <libpq-fe.h>
#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 ======================== */

47
src/engine/sql/events.h Normal file
View File

@ -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__ */