mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
more database hacking
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@2325 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
3b1913a29f
commit
ccf1944f3a
@ -21,12 +21,21 @@
|
|||||||
|
|
||||||
static short module = MOD_BACKEND;
|
static short module = MOD_BACKEND;
|
||||||
|
|
||||||
/* hack alert -- this is the query buffer, it might be too small.
|
|
||||||
we need to make it dynamic sized */
|
|
||||||
#define QBUFSIZE 16350
|
|
||||||
|
|
||||||
/* ============================================================= */
|
/* ============================================================= */
|
||||||
|
|
||||||
|
#define SEND_QUERY(be) { \
|
||||||
|
int rc; \
|
||||||
|
rc = PQsendQuery (be->connection, be->buff); \
|
||||||
|
if (!rc) \
|
||||||
|
{ \
|
||||||
|
/* hack alert -- we need knider, gentler error handling */\
|
||||||
|
PERR("send query failed:\n" \
|
||||||
|
"\t%s", PQerrorMessage(be->connection)); \
|
||||||
|
PQfinish (be->connection); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
#define FLUSH(conn) { \
|
#define FLUSH(conn) { \
|
||||||
PGresult *result; \
|
PGresult *result; \
|
||||||
/* complete/commit the transaction, check the status */ \
|
/* complete/commit the transaction, check the status */ \
|
||||||
@ -48,6 +57,7 @@ static short module = MOD_BACKEND;
|
|||||||
/* ============================================================= */
|
/* ============================================================= */
|
||||||
/* This routine stores the indicated group structure into the database.
|
/* This routine stores the indicated group structure into the database.
|
||||||
* It does *not* chase pointers, traverse the tree, etc.
|
* It does *not* chase pointers, traverse the tree, etc.
|
||||||
|
* It performs no locking.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -55,8 +65,7 @@ pgendStoreOneGroupOnly (PGBackend *be, AccountGroup *grp)
|
|||||||
{
|
{
|
||||||
Account *parent;
|
Account *parent;
|
||||||
const GUID *parent_guid, *grp_guid;
|
const GUID *parent_guid, *grp_guid;
|
||||||
char buff[QBUFSIZE];
|
int i, nacc;
|
||||||
int i, nacc, rc;
|
|
||||||
|
|
||||||
ENTER ("be=%p, grp=%p\n", be, grp);
|
ENTER ("be=%p, grp=%p\n", be, grp);
|
||||||
if (!be || !grp) return;
|
if (!be || !grp) return;
|
||||||
@ -70,7 +79,7 @@ pgendStoreOneGroupOnly (PGBackend *be, AccountGroup *grp)
|
|||||||
Account *acc = xaccGroupGetAccount(grp, i);
|
Account *acc = xaccGroupGetAccount(grp, i);
|
||||||
|
|
||||||
/* hack alert -- values should be escaped so that no '' apear in them */
|
/* hack alert -- values should be escaped so that no '' apear in them */
|
||||||
snprintf (buff, QBUFSIZE,
|
snprintf (be->buff, be->bufflen,
|
||||||
"INSERT INTO gncGroup "
|
"INSERT INTO gncGroup "
|
||||||
"(groupGuid, parentGuid, childGuid)"
|
"(groupGuid, parentGuid, childGuid)"
|
||||||
" values "
|
" values "
|
||||||
@ -79,14 +88,8 @@ pgendStoreOneGroupOnly (PGBackend *be, AccountGroup *grp)
|
|||||||
guid_to_string(parent_guid),
|
guid_to_string(parent_guid),
|
||||||
guid_to_string(xaccAccountGetGUID (acc))
|
guid_to_string(xaccAccountGetGUID (acc))
|
||||||
);
|
);
|
||||||
rc = PQsendQuery (be->connection, buff);
|
|
||||||
if (!rc)
|
SEND_QUERY(be);
|
||||||
{
|
|
||||||
PERR("send query failed:\n"
|
|
||||||
"\t%s", PQerrorMessage(be->connection));
|
|
||||||
PQfinish (be->connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* complete/commit the transaction, check the status */
|
/* complete/commit the transaction, check the status */
|
||||||
FLUSH(be->connection);
|
FLUSH(be->connection);
|
||||||
@ -95,16 +98,88 @@ pgendStoreOneGroupOnly (PGBackend *be, AccountGroup *grp)
|
|||||||
LEAVE ("\n");
|
LEAVE ("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================= */
|
||||||
|
/* This routine stores the indicated account structure into the database.
|
||||||
|
* It does *not* chase pointers, traverse the tree, etc.
|
||||||
|
* It performs no locking.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgendStoreOneAccountOnly (PGBackend *be, Account *acct)
|
||||||
|
{
|
||||||
|
|
||||||
|
ENTER ("be=%p, acct=%p\n", be, acct);
|
||||||
|
if (!be || !acct) return;
|
||||||
|
|
||||||
|
/* hack alert -- values should be escaped so that no '' apear in them */
|
||||||
|
snprintf (be->buff, be->bufflen,
|
||||||
|
"INSERT INTO gncAccount "
|
||||||
|
"(accountGuid, parentGuid, childrenGuid, "
|
||||||
|
"accountName, accountCode, description, notes, "
|
||||||
|
"type, currency, security)"
|
||||||
|
" values "
|
||||||
|
"('%s', '%s', '%s', '%s', '%s', '%s', '%s', "
|
||||||
|
"%d, '%s', '%s');",
|
||||||
|
guid_to_string(xaccAccountGetGUID (acct)),
|
||||||
|
guid_to_string(xaccGroupGetGUID (xaccAccountGetParent(acct))),
|
||||||
|
guid_to_string(xaccGroupGetGUID (xaccAccountGetChildren(acct))),
|
||||||
|
xaccAccountGetName (acct),
|
||||||
|
xaccAccountGetCode (acct),
|
||||||
|
xaccAccountGetDescription (acct),
|
||||||
|
xaccAccountGetNotes (acct),
|
||||||
|
xaccAccountGetType (acct),
|
||||||
|
xaccAccountGetCurrency (acct),
|
||||||
|
xaccAccountGetSecurity (acct)
|
||||||
|
);
|
||||||
|
|
||||||
|
SEND_QUERY (be);
|
||||||
|
|
||||||
|
/* complete/commit the transaction, check the status */
|
||||||
|
FLUSH(be->connection);
|
||||||
|
|
||||||
|
LEAVE ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================= */
|
||||||
|
/* This routine stores the indicated transaction structure into the database.
|
||||||
|
* It does *not* chase pointers, traverse the tree, etc.
|
||||||
|
* It performs no locking.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgendStoreOneTransactionOnly (PGBackend *be, Transaction *trans)
|
||||||
|
{
|
||||||
|
|
||||||
|
ENTER ("be=%p, trans=%p\n", be, trans);
|
||||||
|
if (!be || !trans) return;
|
||||||
|
|
||||||
|
/* hack alert -- values should be escaped so that no '' apear in them */
|
||||||
|
snprintf (be->buff, be->bufflen,
|
||||||
|
"INSERT INTO gncTransaction "
|
||||||
|
"(transGuid, num, description)"
|
||||||
|
" values "
|
||||||
|
"('%s', '%s', '%s');",
|
||||||
|
guid_to_string(xaccTransGetGUID (trans)),
|
||||||
|
xaccTransGetNum (trans),
|
||||||
|
xaccTransGetDescription (trans)
|
||||||
|
);
|
||||||
|
|
||||||
|
SEND_QUERY (be);
|
||||||
|
|
||||||
|
/* complete/commit the transaction, check the status */
|
||||||
|
FLUSH(be->connection);
|
||||||
|
|
||||||
|
LEAVE ("\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================= */
|
/* ============================================================= */
|
||||||
/* this routine stores the indicated split in the database
|
/* this routine stores the indicated split in the database
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pgendStoreSplit (PGBackend *be, Split *split)
|
pgendStoreOneSplitOnly (PGBackend *be, Split *split)
|
||||||
{
|
{
|
||||||
Timespec ts;
|
Timespec ts;
|
||||||
char buff[QBUFSIZE];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
ENTER ("be=%p, split=%p\n", be, split);
|
ENTER ("be=%p, split=%p\n", be, split);
|
||||||
if (!be || !split) return;
|
if (!be || !split) return;
|
||||||
@ -113,7 +188,7 @@ pgendStoreSplit (PGBackend *be, Split *split)
|
|||||||
xaccSplitGetDateReconciledTS (split, &ts);
|
xaccSplitGetDateReconciledTS (split, &ts);
|
||||||
|
|
||||||
/* hack alert -- values should be escaped so that no '' apear in them */
|
/* hack alert -- values should be escaped so that no '' apear in them */
|
||||||
snprintf (buff, QBUFSIZE,
|
snprintf (be->buff, be->bufflen,
|
||||||
"INSERT INTO gncEntry "
|
"INSERT INTO gncEntry "
|
||||||
"(entryGuid, accountGuid, transGuid, memo, action,"
|
"(entryGuid, accountGuid, transGuid, memo, action,"
|
||||||
"reconciled, amount, share_price)"
|
"reconciled, amount, share_price)"
|
||||||
@ -128,14 +203,8 @@ pgendStoreSplit (PGBackend *be, Split *split)
|
|||||||
xaccSplitGetShareAmount(split),
|
xaccSplitGetShareAmount(split),
|
||||||
xaccSplitGetSharePrice(split)
|
xaccSplitGetSharePrice(split)
|
||||||
);
|
);
|
||||||
rc = PQsendQuery (be->connection, buff);
|
|
||||||
if (!rc)
|
SEND_QUERY (be);
|
||||||
{
|
|
||||||
PERR("send query failed:\n"
|
|
||||||
"\t%s", PQerrorMessage(be->connection));
|
|
||||||
PQfinish (be->connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* complete/commit the transaction, check the status */
|
/* complete/commit the transaction, check the status */
|
||||||
FLUSH(be->connection);
|
FLUSH(be->connection);
|
||||||
@ -143,6 +212,80 @@ pgendStoreSplit (PGBackend *be, Split *split)
|
|||||||
LEAVE ("\n");
|
LEAVE ("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================= */
|
||||||
|
/* This routine traverses the group structure and stores it into
|
||||||
|
* the database. The NoLock version doesn't lock up the tables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
traverse_cb (Transaction *trans, void *cb_data)
|
||||||
|
{
|
||||||
|
PGBackend *be = (PGBackend *) cb_data;
|
||||||
|
int i, nsplits;
|
||||||
|
|
||||||
|
if (!be || !trans) return;
|
||||||
|
|
||||||
|
pgendStoreOneTransactionOnly (be, trans);
|
||||||
|
|
||||||
|
/* walk over the list of splits */
|
||||||
|
nsplits = xaccTransGetNumSplits (trans);
|
||||||
|
for (i=0; i<nsplits; i++) {
|
||||||
|
Split * s = xaccTransGetSplit (trans, i);
|
||||||
|
pgendStoreOneSplitOnly (be, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgendStoreGroupNoLock (PGBackend *be, AccountGroup *grp)
|
||||||
|
{
|
||||||
|
int i, nacc;
|
||||||
|
|
||||||
|
if (!be || !grp) return;
|
||||||
|
|
||||||
|
/* first, store the top-group */
|
||||||
|
pgendStoreOneGroupOnly (be, grp);
|
||||||
|
|
||||||
|
/* next, walk the account tree, and store subaccounts */
|
||||||
|
nacc = xaccGroupGetNumAccounts(grp);
|
||||||
|
|
||||||
|
for (i=0; i<nacc; i++) {
|
||||||
|
AccountGroup *subgrp;
|
||||||
|
Account *acc = xaccGroupGetAccount(grp, i);
|
||||||
|
pgendStoreOneAccountOnly (be, acc);
|
||||||
|
|
||||||
|
/* recursively walk to child accounts */
|
||||||
|
subgrp = xaccAccountGetChildren (acc);
|
||||||
|
if (subgrp) pgendStoreGroupNoLock(be, subgrp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
pgendStoreGroup (PGBackend *be, AccountGroup *grp)
|
||||||
|
{
|
||||||
|
ENTER ("be=%p, grp=%p\n", be, grp);
|
||||||
|
if (!be || !grp) return;
|
||||||
|
|
||||||
|
/* lock it up so that we store atomically */
|
||||||
|
snprintf (be->buff, be->bufflen, "BEGIN;");
|
||||||
|
SEND_QUERY (be);
|
||||||
|
FLUSH(be->connection);
|
||||||
|
|
||||||
|
/* reset the write flags. We use this to amek sure we don't
|
||||||
|
* get caught in infinite recursion */
|
||||||
|
xaccGroupBeginStagedTransactionTraversals(grp);
|
||||||
|
pgendStoreGroupNoLock (be, grp);
|
||||||
|
|
||||||
|
/* recursively walk transactions */
|
||||||
|
xaccGroupStagedTransactionTraversal (grp, 1, traverse_cb, be);
|
||||||
|
|
||||||
|
snprintf (be->buff, be->bufflen, "COMMIT;");
|
||||||
|
SEND_QUERY (be);
|
||||||
|
FLUSH(be->connection);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================= */
|
/* ============================================================= */
|
||||||
/* this routine fills in the structure pointed at by split
|
/* this routine fills in the structure pointed at by split
|
||||||
* with data sucked out of the database
|
* with data sucked out of the database
|
||||||
@ -177,6 +320,9 @@ pgend_session_begin (Session *sess, const char * sessionid)
|
|||||||
{
|
{
|
||||||
PGBackend *be;
|
PGBackend *be;
|
||||||
|
|
||||||
|
if (!sess) return;
|
||||||
|
be = (PGBackend *) xaccSessionGetBackend (sess);
|
||||||
|
|
||||||
ENTER("sessionid=%s\n", sessionid);
|
ENTER("sessionid=%s\n", sessionid);
|
||||||
/* connect to a bogus database ... */
|
/* connect to a bogus database ... */
|
||||||
/* hack alert -- clean this up ... */
|
/* hack alert -- clean this up ... */
|
||||||
@ -195,6 +341,11 @@ pgend_session_begin (Session *sess, const char * sessionid)
|
|||||||
|
|
||||||
DEBUGCMD (PQtrace(be->connection, stderr));
|
DEBUGCMD (PQtrace(be->connection, stderr));
|
||||||
|
|
||||||
|
/* hack alert --- */
|
||||||
|
/* just a quickie place to duimp stuff */
|
||||||
|
xaccGroupSetBackend (xaccSessionGetGroup(sess), &(be->be));
|
||||||
|
pgendStoreGroup (be, xaccSessionGetGroup(sess));
|
||||||
|
|
||||||
LEAVE("\n");
|
LEAVE("\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -214,7 +365,7 @@ pgend_trans_commit_edit (Backend * bend, Transaction * trans)
|
|||||||
nsplits = xaccTransCountSplits (trans);
|
nsplits = xaccTransCountSplits (trans);
|
||||||
for (i=0; i<nsplits; i++) {
|
for (i=0; i<nsplits; i++) {
|
||||||
Split *s = xaccTransGetSplit (trans, i);
|
Split *s = xaccTransGetSplit (trans, i);
|
||||||
pgendStoreSplit (be, s);
|
pgendStoreOneSplitOnly (be, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@ -234,6 +385,10 @@ pgend_trans_commit_edit (Backend * bend, Transaction * trans)
|
|||||||
|
|
||||||
/* ============================================================= */
|
/* ============================================================= */
|
||||||
|
|
||||||
|
/* hack alert -- this is the query buffer, it might be too small.
|
||||||
|
we need to make it dynamic sized */
|
||||||
|
#define QBUFSIZE 16350
|
||||||
|
|
||||||
Backend *
|
Backend *
|
||||||
pgendNew (void)
|
pgendNew (void)
|
||||||
{
|
{
|
||||||
@ -255,6 +410,9 @@ pgendNew (void)
|
|||||||
be->dbName = NULL;
|
be->dbName = NULL;
|
||||||
be->connection = NULL;
|
be->connection = NULL;
|
||||||
|
|
||||||
|
be->buff = malloc (QBUFSIZE);
|
||||||
|
be->bufflen = QBUFSIZE;
|
||||||
|
|
||||||
return (Backend *) be;
|
return (Backend *) be;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@ struct _pgend {
|
|||||||
/* postgres-specific conection data */
|
/* postgres-specific conection data */
|
||||||
char * dbName;
|
char * dbName;
|
||||||
PGconn * connection;
|
PGconn * connection;
|
||||||
|
|
||||||
|
/* scratch space for constructing queries */
|
||||||
|
int bufflen;
|
||||||
|
char *buff;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -10,9 +10,13 @@ CREATE TABLE gncGroup (
|
|||||||
childGuid CHAR(32)
|
childGuid CHAR(32)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- hack alert -- docref ??
|
||||||
|
|
||||||
DROP TABLE gncAccount;
|
DROP TABLE gncAccount;
|
||||||
CREATE TABLE gncAccount (
|
CREATE TABLE gncAccount (
|
||||||
accountGuid CHAR(32) PRIMARY KEY,
|
accountGuid CHAR(32) PRIMARY KEY,
|
||||||
|
parentGuid CHAR(32),
|
||||||
|
childrenGuid CHAR(32),
|
||||||
accountName VARCHAR(40) DEFAULT 'xoxo',
|
accountName VARCHAR(40) DEFAULT 'xoxo',
|
||||||
accountCode VARCHAR(8),
|
accountCode VARCHAR(8),
|
||||||
description VARCHAR(120),
|
description VARCHAR(120),
|
||||||
@ -22,12 +26,16 @@ CREATE TABLE gncAccount (
|
|||||||
security VARCHAR(8)
|
security VARCHAR(8)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- initialize with just enough bogus data to run the demo
|
-- hack alert -- docref ??
|
||||||
INSERT INTO gncaccount (accountguid,accountName,description) values
|
|
||||||
('asdfasdf','banky','some bogo bank');
|
|
||||||
INSERT INTO gncaccount (accountguid,accountName,description) values
|
|
||||||
('aqwerqwer','crebit dedit','bankruptcy follows');
|
|
||||||
|
|
||||||
|
DROP TABLE gncTransaction;
|
||||||
|
CREATE TABLE gncTransaction (
|
||||||
|
transGuid CHAR(32) PRIMARY KEY,
|
||||||
|
date_entered DATETIME,
|
||||||
|
date_posted DATETIME,
|
||||||
|
num VARCHAR(8),
|
||||||
|
description VARCHAR(32)
|
||||||
|
);
|
||||||
|
|
||||||
-- a gncEntry is what we call 'Split' elsewhere in the engine
|
-- a gncEntry is what we call 'Split' elsewhere in the engine
|
||||||
|
|
||||||
@ -44,13 +52,3 @@ CREATE TABLE gncEntry (
|
|||||||
share_price FLOAT8 DEFAULT '0.0'
|
share_price FLOAT8 DEFAULT '0.0'
|
||||||
);
|
);
|
||||||
|
|
||||||
DROP TABLE gncTransaction;
|
|
||||||
CREATE TABLE gncTransaction (
|
|
||||||
transGuid CHAR(32) PRIMARY KEY,
|
|
||||||
date_entered DATETIME,
|
|
||||||
date_posted DATETIME,
|
|
||||||
num VARCHAR(8),
|
|
||||||
description VARCHAR(32)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user