00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #define _GNU_SOURCE
00025
00026 #include "config.h"
00027
00028 #include <glib.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031
00032 #include <libpq-fe.h>
00033
00034 #include "AccountP.h"
00035 #include "qofbackend.h"
00036 #include "qofbackend-p.h"
00037 #include "Group.h"
00038 #include "GroupP.h"
00039 #include "qofbook.h"
00040 #include "qofbook-p.h"
00041 #include "gnc-commodity.h"
00042 #include "gnc-engine-util.h"
00043 #include "gnc-event.h"
00044 #include "gnc-pricedb.h"
00045 #include "guid.h"
00046
00047 #include "account.h"
00048 #include "book.h"
00049 #include "base-autogen.h"
00050 #include "kvp-sql.h"
00051 #include "PostgresBackend.h"
00052 #include "price.h"
00053
00054 static short module = MOD_BACKEND;
00055
00056 #include "putil.h"
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 static void
00088 pgendStoreAccountNoLock (PGBackend *be, Account *acct,
00089 gboolean do_mark,
00090 gboolean do_check_version)
00091 {
00092 const gnc_commodity *com;
00093
00094 if (!be || !acct) return;
00095 if ((FALSE == do_mark) && (FALSE == acct->inst.dirty)) return;
00096
00097 ENTER ("acct=%p, mark=%d", acct, do_mark);
00098
00099 if (do_mark)
00100 {
00101
00102
00103
00104
00105
00106
00107 if (xaccAccountGetMark (acct)) return;
00108 xaccAccountSetMark (acct, 1);
00109 }
00110
00111 if (do_check_version)
00112 {
00113 if (0 < pgendAccountCompareVersion (be, acct)) return;
00114 }
00115 acct->version ++;
00116 acct->version_check = be->version_check;
00117
00118 if ((0 == acct->idata) &&
00119 (FALSE == kvp_frame_is_empty (xaccAccountGetSlots(acct))))
00120 {
00121 acct->idata = pgendNewGUIDidx(be);
00122 }
00123
00124 pgendPutOneAccountOnly (be, acct);
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136 com = xaccAccountGetCommodity (acct);
00137 pgendPutOneCommodityOnly (be, (gnc_commodity *) com);
00138
00139 if (acct->idata)
00140 {
00141 pgendKVPDelete (be, acct->idata);
00142 pgendKVPStore (be, acct->idata, acct->inst.kvp_data);
00143 }
00144 LEAVE(" ");
00145 }
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 void
00162 pgendStoreGroupNoLock (PGBackend *be, AccountGroup *grp,
00163 gboolean do_mark, gboolean do_check_version)
00164 {
00165 GList *start, *node;
00166
00167 if (!be || !grp) return;
00168 ENTER("grp=%p mark=%d", grp, do_mark);
00169
00170
00171 start = xaccGroupGetAccountList (grp);
00172 for (node=start; node; node=node->next)
00173 {
00174 AccountGroup *subgrp;
00175 Account *acc = node->data;
00176
00177 pgendStoreAccountNoLock (be, acc, do_mark, do_check_version);
00178
00179
00180 subgrp = xaccAccountGetChildren (acc);
00181 if (subgrp) pgendStoreGroupNoLock(be, subgrp, do_mark,
00182 do_check_version);
00183 }
00184 LEAVE(" ");
00185 }
00186
00187
00188 void
00189 pgendStoreGroup (PGBackend *be, AccountGroup *grp)
00190 {
00191 char *p;
00192 ENTER ("be=%p, grp=%p", be, grp);
00193 if (!be || !grp) return;
00194
00195
00196 p = "BEGIN;\n"
00197 "LOCK TABLE gncAccount IN EXCLUSIVE MODE;\n"
00198 "LOCK TABLE gncCommodity IN EXCLUSIVE MODE;\n";
00199 SEND_QUERY (be,p, );
00200 FINISH_QUERY(be->connection);
00201
00202
00203
00204 xaccClearMarkDownGr (grp, 0);
00205
00206 pgendStoreGroupNoLock (be, grp, TRUE, TRUE);
00207
00208
00209 xaccClearMarkDownGr (grp, 0);
00210
00211 p = "COMMIT;\n"
00212 "NOTIFY gncAccount;";
00213 SEND_QUERY (be,p, );
00214 FINISH_QUERY(be->connection);
00215 LEAVE(" ");
00216 }
00217
00218
00219
00220
00221
00222
00223
00224
00225 static gpointer
00226 restore_cb (Account *acc, void * cb_data)
00227 {
00228 PGBackend *be = (PGBackend *) cb_data;
00229 if (0 == acc->idata) return NULL;
00230 acc->inst.kvp_data = pgendKVPFetch (be, acc->idata, acc->inst.kvp_data);
00231 return NULL;
00232 }
00233
00234 static void
00235 pgendGetAllAccountKVP (PGBackend *be, AccountGroup *grp)
00236 {
00237 if (!grp) return;
00238
00239 xaccGroupForEachAccount (grp, restore_cb, be, TRUE);
00240 }
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251 typedef struct
00252 {
00253 Account * account;
00254
00255 char * commodity_string;
00256 gboolean need_parent;
00257 GUID parent_guid;
00258 } AccountResolveInfo;
00259
00260 typedef struct
00261 {
00262 QofBook * book;
00263 GList * resolve_info;
00264 } GetAccountData;
00265
00266 static AccountResolveInfo *
00267 get_resolve_info (AccountResolveInfo * ri)
00268 {
00269 if (ri) return ri;
00270 return g_new0 (AccountResolveInfo, 1);
00271 }
00272
00273 static gpointer
00274 get_account_cb (PGBackend *be, PGresult *result, int j, gpointer data)
00275 {
00276 GetAccountData * gad = data;
00277 QofBook * book = gad->book;
00278 Account *parent;
00279 Account *acc;
00280 GUID acct_guid;
00281 char * commodity_string;
00282 gnc_commodity * commodity;
00283 AccountResolveInfo *ri = NULL;
00284
00285 PINFO ("account GUID=%s", DB_GET_VAL("accountGUID",j));
00286
00287 FIND_BOOK (book);
00288
00289
00290 acct_guid = nullguid;
00291 string_to_guid (DB_GET_VAL("accountGUID",j), &acct_guid);
00292
00293 acc = xaccAccountLookup (&acct_guid, book);
00294 if (!acc)
00295 {
00296 acc = xaccMallocAccount(book);
00297 xaccAccountBeginEdit(acc);
00298 xaccAccountSetGUID(acc, &acct_guid);
00299 }
00300 else
00301 {
00302 xaccAccountBeginEdit(acc);
00303 }
00304
00305 commodity_string = DB_GET_VAL("commodity",j);
00306 commodity = gnc_string_to_commodity (commodity_string, book);
00307 if (!commodity)
00308 {
00309 ri = get_resolve_info (ri);
00310
00311 ri->account = acc;
00312 ri->commodity_string = g_strdup (commodity_string);
00313 }
00314
00315 xaccAccountSetName(acc, DB_GET_VAL("accountName",j));
00316 xaccAccountSetDescription(acc, DB_GET_VAL("description",j));
00317 xaccAccountSetCode(acc, DB_GET_VAL("accountCode",j));
00318 xaccAccountSetType(acc, xaccAccountStringToEnum(DB_GET_VAL("type",j)));
00319 if (commodity)
00320 xaccAccountSetCommodity(acc, commodity);
00321 xaccAccountSetVersion(acc, atoi(DB_GET_VAL("version",j)));
00322 acc->idata = atoi(DB_GET_VAL("iguid",j));
00323
00324
00325 PINFO ("parent GUID=%s", DB_GET_VAL("parentGUID",j));
00326 acct_guid = nullguid;
00327 string_to_guid (DB_GET_VAL("parentGUID",j), &acct_guid);
00328 if (guid_equal(guid_null(), &acct_guid))
00329 {
00330
00331
00332 xaccGroupInsertAccount (gnc_book_get_group(book), acc);
00333 }
00334 else
00335 {
00336
00337
00338 parent = xaccAccountLookup (&acct_guid, book);
00339 if (!parent)
00340 {
00341 ri = get_resolve_info (ri);
00342
00343 ri->account = acc;
00344 ri->need_parent = TRUE;
00345 ri->parent_guid = acct_guid;
00346 }
00347 else
00348 {
00349 xaccAccountBeginEdit(parent);
00350 xaccAccountInsertSubAccount(parent, acc);
00351 xaccAccountCommitEdit(parent);
00352 }
00353 }
00354
00355 xaccAccountCommitEdit(acc);
00356
00357 if (ri)
00358 gad->resolve_info = g_list_prepend (gad->resolve_info, ri);
00359
00360 return data;
00361 }
00362
00363 static void
00364 pgendGetAccounts (PGBackend *be, QofBook *book)
00365 {
00366 GetAccountData gad;
00367
00368 gad.book = book ? book : be->book;
00369 gad.resolve_info = NULL;
00370
00371 pgendGetResults (be, get_account_cb, &gad);
00372
00373 while (gad.resolve_info)
00374 {
00375 AccountResolveInfo *ri = gad.resolve_info->data;
00376
00377 gad.resolve_info = g_list_remove (gad.resolve_info, ri);
00378
00379 xaccAccountBeginEdit (ri->account);
00380
00381 if (ri->commodity_string)
00382 {
00383 gnc_commodity * commodity;
00384
00385 pgendGetCommodity (be, ri->commodity_string);
00386 commodity = gnc_string_to_commodity (ri->commodity_string,
00387 xaccAccountGetBook (ri->account));
00388
00389 if (commodity)
00390 {
00391 xaccAccountSetCommodity (ri->account, commodity);
00392 }
00393 else
00394 {
00395 PERR ("Can't find commodity %s", ri->commodity_string);
00396 }
00397
00398 g_free (ri->commodity_string);
00399 ri->commodity_string = NULL;
00400 }
00401
00402 if (ri->need_parent)
00403 {
00404 Account * parent;
00405
00406
00407 parent = xaccAccountLookup (&ri->parent_guid, gad.book);
00408
00409 if (!parent)
00410 parent = pgendCopyAccountToEngine (be, &ri->parent_guid);
00411
00412 if (parent)
00413 {
00414 xaccAccountBeginEdit(parent);
00415 xaccAccountInsertSubAccount(parent, ri->account);
00416 xaccAccountCommitEdit(parent);
00417 }
00418 else
00419 {
00420 PERR ("no such account: %s", guid_to_string (&ri->parent_guid));
00421 }
00422 }
00423
00424 xaccAccountCommitEdit (ri->account);
00425
00426 g_free (ri);
00427 }
00428 }
00429
00430 void
00431 pgendGetAllAccounts (PGBackend *be)
00432 {
00433 QofBookList *node;
00434 char * bufp;
00435
00436 ENTER ("be=%p", be);
00437 if (!be) return;
00438
00439
00440 pgendGetAllBooks (be, be->blist);
00441
00442
00443 pgendGetAllCommodities (be);
00444
00445
00446 bufp = "SELECT * FROM gncAccount;";
00447 SEND_QUERY (be, bufp, );
00448 pgendGetAccounts (be, NULL);
00449
00450 for (node=be->blist; node; node=node->next)
00451 {
00452 QofBook *book = node->data;
00453 AccountGroup *topgrp = gnc_book_get_group (book);
00454 pgendGetAllAccountKVP (be, topgrp);
00455
00456
00457
00458
00459 xaccGroupMarkSaved (topgrp);
00460 }
00461
00462 LEAVE (" ");
00463 }
00464
00465 void
00466 pgendGetAllAccountsInBook (PGBackend *be, QofBook *book)
00467 {
00468 char *p, buff[400];
00469 AccountGroup *topgrp;
00470
00471 ENTER ("be=%p", be);
00472 if (!be || !book) return;
00473
00474
00475 pgendGetAllCommodities (be);
00476
00477
00478
00479 p = buff;
00480 p = stpcpy (p, "SELECT * FROM gncAccount WHERE bookGuid='");
00481 p = guid_to_string_buff (qof_book_get_guid(book), p);
00482 p = stpcpy (p, "';");
00483 SEND_QUERY (be, buff, );
00484 pgendGetAccounts (be, book);
00485
00486 topgrp = gnc_book_get_group (book);
00487 pgendGetAllAccountKVP (be, topgrp);
00488
00489
00490
00491
00492 xaccGroupMarkSaved (topgrp);
00493
00494 LEAVE (" ");
00495 }
00496
00497
00498
00499
00500 Account *
00501 pgendCopyAccountToEngine (PGBackend *be, const GUID *acct_guid)
00502 {
00503 char *pbuff;
00504 Account *acc = NULL;
00505 int engine_data_is_newer = 0;
00506
00507 ENTER ("be=%p", be);
00508 if (!be || !acct_guid) return 0;
00509
00510
00511 gnc_engine_suspend_events();
00512 pgendDisable(be);
00513
00514
00515 acc = pgendAccountLookup (be, acct_guid);
00516
00517 if (!acc)
00518 {
00519 engine_data_is_newer = -1;
00520 }
00521 else
00522 {
00523
00524
00525 if (MAX_VERSION_AGE >= be->version_check - acc->version_check)
00526 {
00527 PINFO ("fresh data, skip check");
00528 engine_data_is_newer = 0;
00529 }
00530 else
00531 {
00532 engine_data_is_newer = - pgendAccountCompareVersion (be, acc);
00533 }
00534 }
00535
00536 if (0 > engine_data_is_newer)
00537 {
00538
00539 pbuff = be->buff;
00540 pbuff[0] = 0;
00541 pbuff = stpcpy (pbuff,
00542 "SELECT * FROM gncAccount WHERE accountGuid='");
00543 pbuff = guid_to_string_buff(acct_guid, pbuff);
00544 pbuff = stpcpy (pbuff, "';");
00545
00546 SEND_QUERY (be,be->buff, 0);
00547 pgendGetAccounts (be, NULL);
00548 acc = pgendAccountLookup (be, acct_guid);
00549
00550
00551 if (acc)
00552 {
00553 if (acc->idata)
00554 {
00555 acc->inst.kvp_data = pgendKVPFetch (be, acc->idata, acc->inst.kvp_data);
00556 }
00557
00558 acc->version_check = be->version_check;
00559 }
00560 }
00561
00562
00563 pgendEnable(be);
00564 gnc_engine_resume_events();
00565
00566 LEAVE (" ");
00567 return acc;
00568 }
00569
00570
00571
00572
00573
00574
00575
00576 void
00577 pgend_account_commit_edit (QofBackend * bend,
00578 Account * acct)
00579 {
00580 AccountGroup *parent;
00581 char *p;
00582 QofBackendError err;
00583 PGBackend *be = (PGBackend *)bend;
00584
00585 ENTER ("be=%p, acct=%p", be, acct);
00586 if (!be || !acct) return;
00587
00588 if (FALSE == acct->inst.dirty)
00589 {
00590 parent = xaccAccountGetParent(acct);
00591 if (parent) parent->saved = 1;
00592 LEAVE ("account not written because not dirty");
00593 return;
00594 }
00595
00596
00597
00598 p = "BEGIN;\n"
00599 "LOCK TABLE gncAccount IN EXCLUSIVE MODE;\n"
00600 "LOCK TABLE gncCommodity IN EXCLUSIVE MODE;\n";
00601
00602 SEND_QUERY (be,p,);
00603 FINISH_QUERY(be->connection);
00604
00605
00606
00607
00608 if (0 < pgendAccountCompareVersion (be, acct))
00609 {
00610 acct->inst.do_free = FALSE;
00611 p = "ROLLBACK;";
00612 SEND_QUERY (be,p,);
00613 FINISH_QUERY(be->connection);
00614
00615
00616
00617 PWARN(" account data in engine is newer\n"
00618 " account must be rolled back. This function\n"
00619 " is not completely implemented !! \n");
00620 qof_backend_set_error (&be->be, ERR_BACKEND_MODIFIED);
00621 LEAVE ("rolled back");
00622 return;
00623 }
00624 acct->version ++;
00625 acct->version_check = be->version_check;
00626
00627 if (acct->inst.do_free)
00628 {
00629 const GUID *guid = xaccAccountGetGUID(acct);
00630 pgendKVPDelete (be, acct->idata);
00631
00632 p = be->buff; *p = 0;
00633 p = stpcpy (p, "DELETE FROM gncAccount WHERE accountGuid='");
00634 p = guid_to_string_buff (guid, p);
00635 p = stpcpy (p, "';");
00636 err = sendQuery (be,be->buff);
00637 if (err == ERR_BACKEND_NO_ERR) {
00638 err = finishQuery(be);
00639 if (err > 0)
00640 pgendStoreAuditAccount (be, acct, SQL_DELETE);
00641 }
00642 }
00643 else
00644 {
00645 pgendStoreAccountNoLock (be, acct, FALSE, FALSE);
00646 }
00647
00648 p = "COMMIT;\n"
00649 "NOTIFY gncAccount;";
00650 SEND_QUERY (be,p,);
00651 FINISH_QUERY(be->connection);
00652
00653
00654
00655
00656
00657
00658 parent = xaccAccountGetParent(acct);
00659 if (parent) parent->saved = 1;
00660 LEAVE ("commited");
00661 return;
00662 }
00663
00664