00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "config.h"
00037 #include <glib.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <sys/types.h>
00042
00043 #include <libpq-fe.h>
00044
00045 #include "Account.h"
00046 #include "AccountP.h"
00047 #include "qof.h"
00048 #include "gnc-commodity.h"
00049
00050 #include "builder.h"
00051 #include "checkpoint.h"
00052 #include "escape.h"
00053
00054 #include "putil.h"
00055
00056 static QofLogModule log_module = GNC_MOD_BACKEND;
00057
00058
00059
00060
00061 #include "check-autogen.h"
00062 #include "check-autogen.c"
00063
00064
00065
00066
00067 static void
00068 pgendAccountRecomputeAllCheckpoints (PGBackend *be, const GUID *acct_guid)
00069 {
00070 Timespec this_ts, next_ts;
00071 #ifndef HAVE_GLIB29
00072 GMemChunk *chunk;
00073 #endif
00074 GList *node, *checkpoints = NULL;
00075 PGresult *result;
00076 Checkpoint *bp;
00077 char *p;
00078 int i;
00079 int nck;
00080 Account *acc;
00081 const char *commodity_name, *guid_string;
00082
00083 if (!be) return;
00084 ENTER("be=%p", be);
00085
00086 guid_string = guid_to_string (acct_guid);
00087 acc = pgendAccountLookup (be, acct_guid);
00088 commodity_name =
00089 gnc_commodity_get_unique_name (xaccAccountGetCommodity(acc));
00090
00091 #ifndef HAVE_GLIB29
00092 chunk = g_mem_chunk_create (Checkpoint, 300, G_ALLOC_ONLY);
00093 #endif
00094
00095
00096
00097
00098 p = "BEGIN WORK;\n"
00099 "LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
00100 "LOCK TABLE gncSplit IN SHARE MODE;\n";
00101 SEND_QUERY (be,p, );
00102 FINISH_QUERY(be->connection);
00103
00104
00105
00106
00107 p = be->buff; *p = 0;
00108 p = stpcpy (p, "DELETE FROM gncCheckpoint WHERE accountGuid='");
00109 p = guid_to_string_buff (acct_guid, p);
00110 p = stpcpy (p, "';");
00111 SEND_QUERY (be,be->buff, );
00112 FINISH_QUERY(be->connection);
00113
00114
00115 #ifdef HAVE_GLIB29
00116 bp = g_slice_alloc0 (sizeof(Checkpoint));
00117 #else
00118 bp = g_chunk_new0 (Checkpoint, chunk);
00119 #endif
00120 checkpoints = g_list_prepend (checkpoints, bp);
00121 this_ts = gnc_iso8601_to_timespec_gmt (CK_EARLIEST_DATE);
00122 bp->date_start = this_ts;
00123 bp->account_guid = acct_guid;
00124 bp->commodity = commodity_name;
00125
00126
00127 nck = MIN_CHECKPOINT_COUNT;
00128 while (1)
00129 {
00130 p = be->buff; *p = 0;
00131 p = stpcpy (p, "SELECT gncTransaction.date_posted"
00132 " FROM gncTransaction, gncSplit"
00133 " WHERE"
00134 " gncSplit.transguid = gncTransaction.transguid AND"
00135 " gncSplit.accountguid='");
00136 p = stpcpy (p, guid_string);
00137 p = stpcpy (p, "'"
00138 " ORDER BY gncTransaction.date_posted ASC"
00139 " LIMIT 2 OFFSET ");
00140 p += sprintf (p, "%d", nck);
00141 p = stpcpy (p, ";");
00142 SEND_QUERY (be,be->buff, );
00143
00144 i=0;
00145 do {
00146 GET_RESULTS (be->connection, result);
00147 {
00148 int jrows;
00149 int ncols = PQnfields (result);
00150 jrows = PQntuples (result);
00151 PINFO ("query result %d has %d rows and %d cols",
00152 i, jrows, ncols);
00153
00154 if (0 == jrows) {
00155 FINISH_QUERY(be->connection);
00156 goto done;
00157 }
00158
00159 if (0 == i) this_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",0));
00160 if (2 == jrows) {
00161 next_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",1));
00162 } else if (1 == i) {
00163 next_ts = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_posted",0));
00164 }
00165 PQclear (result);
00166 i++;
00167 }
00168 } while (result);
00169
00170
00171
00172 this_ts.tv_sec += 10;
00173 if (timespec_cmp (&this_ts, &next_ts) < 0)
00174 {
00175
00176
00177 this_ts.tv_sec -= 5;
00178 bp->date_end = this_ts;
00179
00180
00181 #ifdef HAVE_GLIB29
00182 bp = g_slice_alloc0 (sizeof(Checkpoint));
00183 #else
00184 bp = g_chunk_new0 (Checkpoint, chunk);
00185 #endif
00186 checkpoints = g_list_prepend (checkpoints, bp);
00187 bp->date_start = this_ts;
00188 bp->account_guid = acct_guid;
00189 bp->commodity = commodity_name;
00190 nck += MIN_CHECKPOINT_COUNT;
00191 }
00192 else
00193 {
00194
00195 nck += 1;
00196 }
00197 }
00198
00199 done:
00200
00201
00202 this_ts = gnc_iso8601_to_timespec_gmt (CK_LAST_DATE);
00203 bp->date_end = this_ts;
00204
00205
00206 for (node = checkpoints; node; node = node->next)
00207 {
00208 bp = (Checkpoint *) node->data;
00209 pgendStoreOneCheckpointOnly (be, bp, SQL_INSERT);
00210 }
00211
00212 g_list_free (checkpoints);
00213 #ifndef HAVE_GLIB29
00214 g_mem_chunk_destroy (chunk);
00215 #endif
00216
00217
00218
00219 p = be->buff; *p = 0;
00220 p = stpcpy (p, "UPDATE gncCheckpoint SET "
00221 " balance = (gncsubtotalbalance (accountGuid, date_start, date_end )),"
00222 " cleared_balance = (gncsubtotalclearedbalance (accountGuid, date_start, date_end )),"
00223 " reconciled_balance = (gncsubtotalreconedbalance (accountGuid, date_start, date_end )) "
00224 " WHERE accountGuid='");
00225 p = stpcpy (p, guid_string);
00226 p = stpcpy (p, "';\n");
00227 p = stpcpy (p, "COMMIT WORK;\n"
00228 "NOTIFY gncCheckpoint;\n");
00229 SEND_QUERY (be,be->buff, );
00230 FINISH_QUERY(be->connection);
00231 }
00232
00233
00234
00235
00236 void
00237 pgendAccountTreeRecomputeAllCheckpoints (PGBackend *be, Account *parent)
00238 {
00239 GList *acclist, *node;
00240
00241 pgendAccountRecomputeAllCheckpoints (be, xaccAccountGetGUID(parent));
00242
00243 acclist = gnc_account_get_descendants(parent);
00244 for (node = acclist; node; node=node->next)
00245 {
00246 Account *acc = (Account *) node->data;
00247 pgendAccountRecomputeAllCheckpoints (be, xaccAccountGetGUID(acc));
00248 }
00249 g_list_free (acclist);
00250 }
00251
00252
00253
00254
00255 void
00256 pgendAccountRecomputeOneCheckpoint (PGBackend *be, Account *acc, Timespec ts)
00257 {
00258 char *p, dbuf[80];
00259
00260 gnc_timespec_to_iso8601_buff (ts, dbuf);
00261
00262 p = be->buff; *p = 0;
00263 p = stpcpy (p, "BEGIN WORK;\n"
00264 "LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
00265 "LOCK TABLE gncSplit IN SHARE MODE;\n"
00266 "UPDATE gncCheckpoint SET "
00267 " balance = (gncsubtotalbalance (accountGuid, date_start, date_end )),"
00268 " cleared_balance = (gncsubtotalclearedbalance (accountGuid, date_start, date_end )),"
00269 " reconciled_balance = (gncsubtotalreconedbalance (accountGuid, date_start, date_end )) "
00270 " WHERE accountGuid='");
00271 p = guid_to_string_buff (xaccAccountGetGUID(acc), p);
00272 p = stpcpy (p, "' AND date_start <= '");
00273 p = stpcpy (p, dbuf);
00274 p = stpcpy (p, "' AND date_end > '");
00275 p = stpcpy (p, dbuf);
00276 p = stpcpy (p, "';\n");
00277
00278 p = stpcpy (p, "COMMIT WORK;\n"
00279 "NOTIFY gncCheckpoint;\n");
00280 SEND_QUERY (be,be->buff, );
00281 FINISH_QUERY(be->connection);
00282 }
00283
00284
00285
00286
00287 void
00288 pgendTransactionRecomputeCheckpoints (PGBackend *be, Transaction *trans)
00289 {
00290 char *p;
00291
00292 p = be->buff; *p = 0;
00293 p = stpcpy (p, "BEGIN WORK;\n"
00294 "LOCK TABLE gncCheckpoint IN ACCESS EXCLUSIVE MODE;\n"
00295 "LOCK TABLE gncTransaction IN SHARE MODE;\n"
00296 "LOCK TABLE gncSplit IN SHARE MODE;\n"
00297 "UPDATE gncCheckpoint SET "
00298 " balance = (gncsubtotalbalance (gncSplit.accountGuid, date_start, date_end )),"
00299 " cleared_balance = (gncsubtotalclearedbalance (gncSplit.accountGuid, date_start, date_end )),"
00300 " reconciled_balance = (gncsubtotalreconedbalance (gncSplit.accountGuid, date_start, date_end )) "
00301 " WHERE gncSplit.transGuid = '");
00302 p = guid_to_string_buff (xaccTransGetGUID(trans), p);
00303 p = stpcpy (p, "' AND gncTransaction.transGuid = gncSplit.transGuid "
00304 " AND gncCheckpoint.accountGuid = gncSplit.accountGuid "
00305 " AND date_start <= gncTransaction.date_posted "
00306 " AND date_end > gncTransaction.date_posted;\n"
00307 "COMMIT WORK;\n"
00308 "NOTIFY gncCheckpoint;\n");
00309 SEND_QUERY (be,be->buff, );
00310 FINISH_QUERY(be->connection);
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320 static gpointer
00321 get_checkpoint_cb (PGBackend *be, PGresult *result, int j, gpointer data)
00322 {
00323 Checkpoint *chk = (Checkpoint *) data;
00324 chk->balance = strtoll(DB_GET_VAL("baln", j), NULL, 0);
00325 chk->cleared_balance = strtoll(DB_GET_VAL("cleared_baln", j), NULL, 0);
00326 chk->reconciled_balance = strtoll(DB_GET_VAL("reconed_baln", j), NULL, 0);
00327 return data;
00328 }
00329
00330 static gpointer
00331 get_checkpoint_date_cb (PGBackend *be, PGresult *result, int j, gpointer data)
00332 {
00333 Checkpoint *chk = (Checkpoint *) data;
00334 chk->date_start = gnc_iso8601_to_timespec_gmt (DB_GET_VAL("date_start", j));
00335 return data;
00336 }
00337
00338 static void
00339 pgendAccountGetCheckpoint (PGBackend *be, Checkpoint *chk)
00340 {
00341 sqlEscape *escape;
00342 char guid_str[80], end_str[80];
00343 char * p;
00344
00345 if (!be || !chk) return;
00346 ENTER("be=%p", be);
00347
00348 escape = sqlEscape_new ();
00349
00350 guid_to_string_buff (chk->account_guid, guid_str);
00351 gnc_timespec_to_iso8601_buff (chk->date_end, end_str);
00352
00353
00354 p = be->buff; *p = 0;
00355 p = stpcpy (p, "SELECT sum(balance) AS baln, "
00356 " sum(cleared_balance) AS cleared_baln, "
00357 " sum(reconciled_balance) AS reconed_baln "
00358 " FROM gncCheckpoint "
00359 " WHERE accountGuid='");
00360 p = stpcpy (p, guid_str);
00361 p = stpcpy (p, "' AND commodity='");
00362 p = stpcpy (p, sqlEscapeString (escape, chk->commodity));
00363 p = stpcpy (p, "' AND date_end <'");
00364 p = stpcpy (p, end_str);
00365 p = stpcpy (p, "';");
00366 SEND_QUERY (be,be->buff, );
00367
00368 sqlEscape_destroy (escape);
00369 escape = NULL;
00370
00371 pgendGetResults (be, get_checkpoint_cb, chk);
00372
00373
00374
00375 p = be->buff; *p = 0;
00376 p = stpcpy (p, "SELECT date_start FROM gncCheckpoint "
00377 " WHERE accountGuid='");
00378 p = stpcpy (p, guid_str);
00379 p = stpcpy (p, "' AND date_start < '");
00380 p = stpcpy (p, end_str);
00381 p = stpcpy (p, "' ORDER BY date_start DESC LIMIT 1;");
00382 SEND_QUERY (be,be->buff, );
00383
00384
00385 chk->date_start = gnc_iso8601_to_timespec_gmt (CK_EARLIEST_DATE);
00386 pgendGetResults (be, get_checkpoint_date_cb, chk);
00387
00388 LEAVE("be=%p", be);
00389 }
00390
00391
00392
00393
00394 static void
00395 pgendAccountGetPartialBalance (PGBackend *be, Checkpoint *chk)
00396 {
00397 char guid_str[80], start_str[80], end_str[80];
00398 char * p;
00399
00400 if (!be || !chk) return;
00401 ENTER("be=%p", be);
00402
00403 guid_to_string_buff (chk->account_guid, guid_str);
00404 gnc_timespec_to_iso8601_buff (chk->date_start, start_str);
00405 gnc_timespec_to_iso8601_buff (chk->date_end, end_str);
00406
00407
00408 p = be->buff; *p = 0;
00409 p = stpcpy (p, "SELECT gncSubtotalBalance ('");
00410 p = stpcpy (p, guid_str);
00411 p = stpcpy (p, "', '");
00412 p = stpcpy (p, start_str);
00413 p = stpcpy (p, "', '");
00414 p = stpcpy (p, end_str);
00415 p = stpcpy (p, "') AS baln, "
00416 " gncSubtotalClearedBalance ('");
00417 p = stpcpy (p, guid_str);
00418 p = stpcpy (p, "', '");
00419 p = stpcpy (p, start_str);
00420 p = stpcpy (p, "', '");
00421 p = stpcpy (p, end_str);
00422 p = stpcpy (p, "') AS cleared_baln, "
00423 " gncSubtotalReconedBalance ('");
00424 p = stpcpy (p, guid_str);
00425 p = stpcpy (p, "', '");
00426 p = stpcpy (p, start_str);
00427 p = stpcpy (p, "', '");
00428 p = stpcpy (p, end_str);
00429 p = stpcpy (p, "') AS reconed_baln;");
00430
00431 SEND_QUERY (be,be->buff, );
00432
00433 pgendGetResults (be, get_checkpoint_cb, chk);
00434
00435 LEAVE("be=%p", be);
00436 }
00437
00438
00439
00440
00441 void
00442 pgendAccountGetBalance (PGBackend *be, Account *acc, Timespec as_of_date)
00443 {
00444 Checkpoint chk;
00445 const gnc_commodity *com;
00446 gint64 b, cl_b, rec_b, deno;
00447 gnc_numeric baln;
00448 gnc_numeric cleared_baln;
00449 gnc_numeric reconciled_baln;
00450
00451 if (!be || !acc) return;
00452 ENTER("be=%p", be);
00453
00454
00455 chk.date_end = as_of_date;
00456
00457 com = xaccAccountGetCommodity(acc);
00458 if (!com)
00459 {
00460 PERR("account %s has no commodity",
00461 guid_to_string (xaccAccountGetGUID (acc)));
00462 return;
00463 }
00464
00465 chk.commodity = gnc_commodity_get_unique_name(com);
00466 chk.account_guid = xaccAccountGetGUID (acc);
00467 chk.balance = 0;
00468 chk.cleared_balance = 0;
00469 chk.reconciled_balance = 0;
00470
00471
00472 pgendAccountGetCheckpoint (be, &chk);
00473
00474 b = chk.balance;
00475 cl_b = chk.cleared_balance;
00476 rec_b = chk.reconciled_balance;
00477 deno = gnc_commodity_get_fraction (com);
00478
00479 {
00480 char buf[80];
00481 gnc_timespec_to_iso8601_buff (chk.date_start, buf);
00482 DEBUG("%s balance to %s baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT " clr=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT " rcn=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
00483 xaccAccountGetDescription (acc), buf,
00484 b, deno, cl_b, deno, rec_b, deno);
00485 }
00486
00487
00488 pgendAccountGetPartialBalance (be, &chk);
00489
00490 b += chk.balance;
00491 cl_b += chk.cleared_balance;
00492 rec_b += chk.reconciled_balance;
00493
00494
00495 baln = gnc_numeric_create (b, deno);
00496 cleared_baln = gnc_numeric_create (cl_b, deno);
00497 reconciled_baln = gnc_numeric_create (rec_b, deno);
00498
00499 g_object_set(acc,
00500 "start-balance", &baln,
00501 "start-cleared-balance", &cleared_baln,
00502 "start-reconcoled-balance", &reconciled_baln,
00503 NULL);
00504
00505 {
00506 char buf[80];
00507 gnc_timespec_to_iso8601_buff (as_of_date, buf);
00508 LEAVE("be=%p %s %s baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT " clr=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT " rcn=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, be,
00509 xaccAccountGetDescription (acc), buf,
00510 b, deno, cl_b, deno, rec_b, deno);
00511 }
00512 }
00513
00514
00515
00516
00517 void
00518 pgendAccountTreeGetAllBalances (PGBackend *be, Account *root,
00519 Timespec as_of_date)
00520 {
00521 GList *acclist, *node;
00522
00523 if (!be || !root) return;
00524 ENTER("be=%p", be);
00525
00526
00527 acclist = gnc_account_get_descendants (root);
00528 for (node=acclist; node; node=node->next)
00529 {
00530 Account *acc = (Account *) node->data;
00531 pgendAccountGetBalance (be, acc, as_of_date);
00532 }
00533
00534 g_list_free (acclist);
00535 LEAVE("be=%p", be);
00536 }
00537
00538