1
/********************************************************************\
2
* price.c -- implements price handling for the postgres backend *
3
* Copyright (c) 2001, 2002 Linas Vepstas <linas@linas.org> *
5
* This program is free software; you can redistribute it and/or *
6
* modify it under the terms of the GNU General Public License as *
7
* published by the Free Software Foundation; either version 2 of *
8
* the License, or (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License*
16
* along with this program; if not, contact: *
18
* Free Software Foundation Voice: +1-617-542-5942 *
19
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20
* Boston, MA 02110-1301, USA gnu@gnu.org *
21
\********************************************************************/
31
#include "gnc-commodity.h"
32
#include "gnc-engine.h"
33
#include "gnc-pricedb.h"
34
#include "gnc-pricedb-p.h"
36
#include "PostgresBackend.h"
37
#include "base-autogen.h"
42
static QofLogModule log_module = GNC_MOD_BACKEND;
44
/* ============================================================= */
45
/* ============================================================= */
46
/* COMMODITIES STUFF */
47
/* ============================================================= */
48
/* ============================================================= */
50
/* This routine restores all commodities in the database.
54
get_commodities_cb (PGBackend *be, PGresult *result, int j, gpointer data)
58
/* stick the commodity in every book ... */
59
for (node=be->blist; node; node=node->next)
62
QofBook *book = node->data;
63
gnc_commodity_table *comtab = gnc_book_get_commodity_table (book);
65
if (!comtab) continue;
67
/* first, lets see if we've already got this one */
68
com = gnc_commodity_table_lookup(comtab,
69
DB_GET_VAL("namespace",j),
70
DB_GET_VAL("mnemonic",j));
74
/* no we don't ... restore it */
75
com = gnc_commodity_new (book,
76
DB_GET_VAL("fullname",j),
77
DB_GET_VAL("namespace",j),
78
DB_GET_VAL("mnemonic",j),
80
atoi(DB_GET_VAL("fraction",j)));
82
gnc_commodity_table_insert (comtab, com);
88
pgendGetAllCommodities (PGBackend *be)
93
ENTER ("be=%p, conn=%p", be, be->connection);
96
p = "SELECT * FROM gncCommodity;";
98
pgendGetResults (be, get_commodities_cb, NULL);
104
pgendGetCommodity (PGBackend *be, const char * unique_name)
109
if (!be || !unique_name) return;
111
ENTER ("be=%p, conn=%p", be, be->connection);
113
escape = sqlEscape_new ();
117
p = stpcpy (p, "SELECT * FROM gncCommodity WHERE gncCommodity.commodity='");
118
p = stpcpy (p, sqlEscapeString (escape, unique_name));
119
p = stpcpy (p, "';");
121
SEND_QUERY (be, be->buff, );
122
pgendGetResults (be, get_commodities_cb, NULL);
124
sqlEscape_destroy (escape);
129
/* ============================================================= */
130
/* ============================================================= */
132
/* ============================================================= */
133
/* ============================================================= */
134
/* store just one price */
137
pgendStorePriceNoLock (PGBackend *be, GNCPrice *pr,
138
gboolean do_check_version)
140
gnc_commodity *modity;
142
if (do_check_version)
144
if (0 < pgendPriceCompareVersion (be, pr)) return;
146
/* be sure to update the version !! */
147
qof_instance_increment_version(pr, be->version_check);
149
/* make sure that we've stored the commodity
150
* and currency before we store the price.
152
modity = gnc_price_get_commodity (pr);
153
pgendPutOneCommodityOnly (be, modity);
155
modity = gnc_price_get_currency (pr);
156
pgendPutOneCommodityOnly (be, modity);
158
pgendPutOnePriceOnly (be, pr);
161
/* ============================================================= */
162
/* store entire price database */
165
foreach_price_cb (GNCPrice *pr, gpointer bend)
167
PGBackend *be = (PGBackend *) bend;
168
gnc_commodity *modity;
171
/* make sure that we've stored the commodity
172
* and currency before we store the price.
173
* We use marks to avoid redundant stores.
175
modity = gnc_price_get_commodity (pr);
176
mark = gnc_commodity_get_mark (modity);
178
pgendPutOneCommodityOnly (be, modity);
179
gnc_commodity_set_mark (modity, 1);
182
modity = gnc_price_get_currency (pr);
183
mark = gnc_commodity_get_mark (modity);
185
pgendPutOneCommodityOnly (be, modity);
186
gnc_commodity_set_mark (modity, 1);
189
pgendPutOnePriceOnly (be, pr);
195
commodity_mark_cb (gnc_commodity *cm, gpointer user_data)
197
gint32 v = ((gint32) GPOINTER_TO_INT(user_data)) & 0xffff;
198
gnc_commodity_set_mark (cm, (gint16) v);
204
pgendStorePriceDBNoLock (PGBackend *be, QofBook *book)
207
gnc_commodity_table *comtab;
209
prdb = gnc_book_get_pricedb(book);
210
comtab = gnc_book_get_commodity_table (book);
212
/* Clear the marks on commodities -- we use this to mark
213
* the thing as 'already stored', avoiding redundant stores */
214
gnc_commodity_table_foreach_commodity (comtab, commodity_mark_cb, 0);
216
gnc_pricedb_foreach_price (prdb, foreach_price_cb,
217
(gpointer) be, FALSE);
219
gnc_commodity_table_foreach_commodity (comtab, commodity_mark_cb, 0);
223
pgendStorePriceDB (PGBackend *be, QofBook *book)
226
ENTER ("be=%p, book=%p", be, book);
227
if (!be || !book) return;
229
/* Lock it up so that we store atomically */
231
"LOCK TABLE gncPrice IN EXCLUSIVE MODE;\n";
233
FINISH_QUERY(be->connection);
235
pgendStorePriceDBNoLock (be, book);
240
FINISH_QUERY(be->connection);
244
/* ============================================================= */
245
/* The pgendGetAllPrices() routine sucks *all* of the
246
* prices out of the database. This is a potential
247
* CPU and memory-burner; its use is not suggested for anything
248
* but single-user mode.
252
get_price_cb (PGBackend *be, PGresult *result, int j, gpointer data)
254
QofBook *book = data;
257
gint32 sql_vers, local_vers;
261
GUID guid = nullguid;
264
gnc_commodity * modity;
268
prdb = gnc_book_get_pricedb(book);
270
/* First, lets see if we've already got this one */
271
string_to_guid (DB_GET_VAL ("priceGuid", j), &guid);
272
pr = gnc_price_lookup (&guid, book);
276
pr = gnc_price_create(book);
277
gnc_price_begin_edit (pr);
278
gnc_price_set_guid (pr, &guid);
284
gnc_price_begin_edit (pr);
288
/* compare versions. Hack alert -- Not sure how to handle failures */
289
sql_vers = atoi (DB_GET_VAL("version",j));
290
local_vers = qof_instance_get_version(pr);
291
if (sql_vers < local_vers) {
292
PERR ("local price version is higher than db !!! local=%d sql=%d",
293
local_vers, sql_vers);
294
gnc_price_commit_edit (pr);
295
gnc_price_unref (pr);
298
qof_instance_set_version (pr, sql_vers);
300
modity = gnc_string_to_commodity (DB_GET_VAL("commodity",j), book);
301
gnc_price_set_commodity (pr, modity);
303
modity = gnc_string_to_commodity (DB_GET_VAL("currency",j), book);
304
gnc_price_set_currency (pr, modity);
306
ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("time",j));
307
gnc_price_set_time (pr, ts);
309
gnc_price_set_source (pr, DB_GET_VAL("source",j));
310
gnc_price_set_typestr (pr, DB_GET_VAL("type",j));
312
num = strtoll (DB_GET_VAL("valueNum", j), NULL, 0);
313
denom = strtoll (DB_GET_VAL("valueDenom", j), NULL, 0);
314
value = gnc_numeric_create (num, denom);
315
gnc_price_set_value (pr, value);
317
if (not_found) gnc_pricedb_add_price(prdb, pr);
318
gnc_price_commit_edit (pr);
319
gnc_price_unref (pr);
326
pgendGetAllPricesInBook (PGBackend *be, QofBook *book)
331
ENTER ("be=%p, conn=%p", be, be->connection);
333
/* first, make sure commodities table is up to date */
334
pgendGetAllCommodities (be);
338
p = stpcpy (p, "SELECT * FROM gncPrice WHERE bookGuid='");
339
p = guid_to_string_buff (qof_book_get_guid(book), p);
340
p = stpcpy (p, "';");
341
SEND_QUERY (be, buff, );
342
pgendGetResults (be, get_price_cb, book);
347
/* ============================================================= */
350
pgendPriceFind (QofBackend *bend, gpointer olook)
352
PGBackend *be = (PGBackend *)bend;
353
GNCPriceLookup *look = (GNCPriceLookup *)olook;
354
const char * commodity_str;
355
const char * currency_str;
359
ENTER ("be=%p, lookup=%p", be, look);
360
if (!be || !look) { LEAVE("(null) args"); return; }
362
/* special case the two-way search in terms of more basic primitives */
363
if (LOOKUP_NEAREST_IN_TIME == look->type)
365
look->type = LOOKUP_LATEST_BEFORE;
366
pgendPriceFind (bend, look);
367
look->type = LOOKUP_EARLIEST_AFTER;
368
pgendPriceFind (bend, look);
373
escape = sqlEscape_new ();
375
commodity_str = gnc_commodity_get_unique_name(look->commodity);
376
currency_str = gnc_commodity_get_unique_name(look->currency);
378
/* don't send events to GUI, don't accept callbacks to backend */
382
/* set up the common part of the query */
383
p = be->buff; *p = 0;
384
p = stpcpy (p, "SELECT * FROM gncPrice"
385
" WHERE commodity='");
386
p = stpcpy (p, sqlEscapeString (escape, commodity_str));
387
p = stpcpy (p, "' ");
390
p = stpcpy (p, "AND currency='");
391
p = stpcpy (p, sqlEscapeString (escape, currency_str));
392
p = stpcpy (p, "' ");
395
PINFO("query = %s", be->buff);
397
sqlEscape_destroy (escape);
403
p = stpcpy (p, "ORDER BY time DESC LIMIT 1;");
406
/* Get all prices for this commodity and currency */
410
p = stpcpy (p, "AND time='");
411
p = gnc_timespec_to_iso8601_buff (look->date, p);
412
p = stpcpy (p, "';");
414
case LOOKUP_NEAREST_IN_TIME:
415
PERR ("this can't possibly happen but it did!!!");
418
case LOOKUP_LATEST_BEFORE:
419
p = stpcpy (p, "AND time <= '");
420
p = gnc_timespec_to_iso8601_buff (look->date, p);
421
p = stpcpy (p, "' ORDER BY time DESC LIMIT 1;");
423
case LOOKUP_EARLIEST_AFTER:
424
p = stpcpy (p, "AND time >= '");
425
p = gnc_timespec_to_iso8601_buff (look->date, p);
426
p = stpcpy (p, "' ORDER BY time ASC LIMIT 1;");
429
PERR ("unknown lookup type %d", look->type);
430
/* re-enable events */
437
SEND_QUERY (be, be->buff, );
438
pgendGetResults (be, get_price_cb, NULL);
440
/* re-enable events */
446
/* ============================================================= */
447
/* ============================================================= */
448
/* HIGHER LEVEL ROUTINES AND BACKEND PROPER */
449
/* ============================================================= */
450
/* ============================================================= */
453
pgend_price_begin_edit (QofBackend * bend, GNCPrice *pr)
455
if (pr && pr->db && qof_instance_get_dirty_flag(pr->db))
457
PERR ("price db is unexpectedly dirty");
463
pgend_price_commit_edit (QofBackend * bend, GNCPrice *pr)
466
PGBackend *be = (PGBackend *)bend;
468
ENTER ("be=%p, price=%p", be, pr);
469
if (!be || !pr) return;
471
/* lock it up so that we query and store atomically */
473
"LOCK TABLE gncPrice IN EXCLUSIVE MODE;\n";
474
SEND_QUERY (be,bufp,);
475
FINISH_QUERY(be->connection);
477
/* check to see that the engine version is equal or newer than
478
* whats in the database. It its not, then some other user has
479
* made changes, and we must roll back. */
480
if (0 < pgendPriceCompareVersion (be, pr))
482
qof_instance_set_destroying(pr, FALSE);
484
SEND_QUERY (be,bufp,);
485
FINISH_QUERY(be->connection);
487
/* hack alert -- we should restore the price data from the
488
* sql back end at this point ! !!! */
489
PWARN(" price data in engine is newer\n"
490
" price must be rolled back. This function\n"
491
" is not completely implemented !! \n");
492
LEAVE ("rolled back");
493
qof_backend_set_error (&be->be, ERR_BACKEND_MODIFIED);
496
/* be sure to update the version !! */
497
qof_instance_increment_version(pr, be->version_check);
499
if (qof_instance_get_destroying(pr))
501
pgendStoreAuditPrice (be, pr, SQL_DELETE);
502
bufp = be->buff; *bufp = 0;
503
bufp = stpcpy (bufp, "DELETE FROM gncPrice WHERE priceGuid='");
504
bufp = guid_to_string_buff (gnc_price_get_guid(pr), bufp);
505
bufp = stpcpy (bufp, "';");
506
PINFO ("%s\n", be->buff ? be->buff : "(null)");
507
SEND_QUERY (be,be->buff, );
508
FINISH_QUERY(be->connection);
512
pgendStorePriceNoLock (be, pr, FALSE);
517
SEND_QUERY (be,bufp,);
518
FINISH_QUERY(be->connection);
521
qof_instance_mark_clean(&pr->db->inst);
527
/* ======================== END OF FILE ======================== */