[base] Rename 'object'->'resource'
[abstract.git] / storage / aaSaveTransaction.cpp
blobad2697f0f587908587315d8edb031006df678eed
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent tw=79 ft=cpp: */
3 /*
4 * Copyright (C) 2007 Sergey Yanovich <ynvich@gmail.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 #include <abstract/aacore.h>
23 #include <string.h>
25 #include "nsCOMPtr.h"
26 #include "nsStringAPI.h"
27 #include "nsEmbedString.h"
29 /* Unfrozen API */
30 #include "unstable/mozIStorageConnection.h"
31 #include "unstable/mozIStorageStatement.h"
33 /* Project includes */
34 #include <abstract/base/aaIFlow.h>
35 #include <abstract/base/aaIFact.h>
36 #include <abstract/base/aaITransaction.h>
37 #include <abstract/storage/aaISaveQuery.h>
38 #include "aaSaveTransaction.h"
39 #include "aaGuard.h"
41 #define AA_READ_RATES "SELECT fact.side, term.side, flow.rate, quote.rate\
42 FROM transfer\
43 LEFT JOIN fact ON transfer.day=(replace(round(julianday(?,'unixepoch')),\
44 '.0','')) AND transfer.event_id=? AND transfer.id=? AND\
45 transfer.day=fact.day AND transfer.event_id=fact.event_id\
46 AND transfer.id=fact.transfer_id\
47 LEFT JOIN flow ON fact.flow_id=flow.id\
48 LEFT JOIN term ON fact.flow_id=term.flow_id AND (fact.side!=term.side OR\
49 fact.side=0)\
50 LEFT JOIN quote ON term.resource_id=quote.resource_id AND\
51 quote.day=(SELECT MAX(day) FROM quote AS list\
52 WHERE list.resource_id=term.resource_id AND list.day<=transfer.day)"
54 #define AA_READ_BALANCES "SELECT fact.side, balance.id, fact.flow_id,\
55 flow.rate, balance.side, balance.amount, balance.value, strftime('%s',\
56 balance.start)\
57 FROM fact\
58 LEFT JOIN transfer ON fact.day == (replace(round(julianday(?,'unixepoch')),\
59 '.0', '')) AND fact.event_id == ? AND fact.transfer_id == ? AND\
60 fact.day == transfer.day AND fact.event_id == transfer.event_id AND\
61 fact.transfer_id == transfer.id\
62 LEFT JOIN flow ON fact.flow_id == flow.id\
63 LEFT JOIN balance ON fact.flow_id == balance.flow_id AND balance.paid IS NULL\
64 WHERE balance.id IS NOT NULL\
65 ORDER BY fact.side DESC, balance.start"
67 #define AA_SAVE_TXN "INSERT INTO txn (day, event_id, transfer_id, value)\
68 VALUES ((replace(round(julianday(?,'unixepoch')),'.0','')), ?, ?, ?)"
70 #define AA_SAVE_BALANCE "INSERT INTO balance (flow_id, side, amount, value,\
71 start) VALUES (?,?,?,?,(replace(round(julianday(?,'unixepoch')),'.0','')))"
73 #define AA_BALANCE_UPDATE "UPDATE balance SET side = ?, amount = ?, value = ?\
74 WHERE id = ?"
76 #define AA_BALANCE_CLEAR "UPDATE balance\
77 SET paid = replace(round(julianday(?,'unixepoch')),'.0','')\
78 WHERE id = ?"
80 /****************** aaFullQuote *************************/
81 aaFullQuote::aaFullQuote(mozIStorageStatement *aStatement)
82 :mReady(PR_FALSE), mType(3)
84 nsresult rv;
85 PRBool isNull;
86 PRInt32 side1, side2;
87 rv = aStatement->GetInt32( 0, &side1 );
88 if (NS_FAILED( rv ))
89 return;
90 rv = aStatement->GetInt32( 1, &side2 );
91 if (NS_FAILED( rv ))
92 return;
93 rv = aStatement->GetDouble( 2, &mRate );
94 if (NS_FAILED( rv ))
95 return;
96 rv = aStatement->GetIsNull( 3, &isNull );
97 if (NS_FAILED( rv ))
98 return;
99 mType = side1 << 1;
100 mType += side2;
101 if (! side2)
102 mRate = 1 / mRate;
103 if ( isNull )
104 return;
105 rv = aStatement->GetDouble( 3, &mQuote );
106 if (NS_FAILED( rv ))
107 return;
108 mReady = PR_TRUE;
111 /***************** aaBalanceRow *************************/
112 aaBalanceRow::aaBalanceRow(const aaBalanceRow& other)
114 memcpy((void *) this, (const void *) &other, sizeof(aaBalanceRow));
117 aaBalanceRow::aaBalanceRow(mozIStorageStatement *aStatement)
118 :mStatus(0)
120 nsresult rv;
121 rv = aStatement->GetInt32( 0, &mFactSide );
122 if (NS_FAILED( rv ))
123 return;
124 rv = aStatement->GetInt64( 1, &mId );
125 if (NS_FAILED( rv ))
126 return;
127 rv = aStatement->GetInt64( 2, &mFlowId );
128 if (NS_FAILED( rv ))
129 return;
130 rv = aStatement->GetDouble( 3, &mFlowRate );
131 if (NS_FAILED( rv ))
132 return;
133 rv = aStatement->GetInt32( 4, &mSide );
134 if (NS_FAILED( rv ))
135 return;
136 rv = aStatement->GetDouble( 5, &mAmount );
137 if (NS_FAILED( rv ))
138 return;
139 rv = aStatement->GetDouble( 6, &mValue );
140 if (NS_FAILED( rv ))
141 return;
142 rv = aStatement->GetInt64( 7, &mStart );
143 if (NS_FAILED( rv ))
144 return;
145 mStart *= 1000000;
146 mPaid = 0;
147 mStatus = SIDE | FLOW | QUOTE;
150 PRBool
151 aaBalanceRow::SetSide(const aaFullQuote& aQuote)
153 double rate;
154 if (NS_UNLIKELY( ! aQuote.GetType() ))
155 return PR_FALSE;
156 rate = aQuote.GetRate();
157 if (rate == 0.0)
158 return PR_FALSE;
159 mFlowRate = rate;
160 mSide = aQuote.GetType() & 1;
161 mFactSide = ! mSide;
162 mStatus = SIDE;
163 return PR_TRUE;
166 PRBool
167 aaBalanceRow::SetFact(aaIFact *aFact)
169 if (NS_UNLIKELY( mStatus != SIDE ))
170 return PR_FALSE;
171 mStatus = FLOW | SIDE;
172 nsCOMPtr<aaIFlow> flow;
173 if ( ! mSide ) {
174 aFact->GetTakeFrom(getter_AddRefs( flow ));
175 } else {
176 aFact->GetGiveTo(getter_AddRefs( flow ));
178 flow->GetId( &mFlowId );
179 aFact->GetAmount( &mAmount );
180 mAmount *= mFlowRate;
181 aFact->GetTime( &mStart );
182 mPaid = 0;
183 return PR_TRUE;
186 void
187 aaBalanceRow::Merge(aaBalanceRow& other)
189 if (other.GetStart() == mStart) {
190 Consume( other );
191 other.Flash();
192 mStatus |= UPDATE;
193 } else {
194 other.Consume( *this );
195 mPaid = other.GetStart();
196 mStatus |= CLEARED;
200 void
201 aaBalanceRow::Consume(aaBalanceRow& other)
203 if (other.GetSide() == mSide) {
204 mAmount += other.GetAmount();
205 mValue += other.GetValue();
206 } else if (!mSide) {
207 mSide = 1;
208 mAmount = other.GetAmount() * mFlowRate - mAmount;
209 mValue = mAmount * other.GetValue() / other.GetAmount();
210 mStatus |= QUOTE;
214 void
215 aaBalanceRow::Flash()
217 mAmount = 0.0;
218 mValue = 0.0;
221 PRBool
222 aaBalanceRow::SetQuote(const aaFullQuote& aQuote)
224 if (NS_UNLIKELY( ! mStatus & FLOW || ! aQuote.IsReady() ))
225 return PR_FALSE;
226 mStatus |= QUOTE;
227 mValue = mAmount * aQuote.GetQuote() / mFlowRate;
228 return PR_TRUE;
231 PRBool
232 aaBalanceRow::SetValue(const double aValue)
234 if (NS_UNLIKELY( ! mStatus & FLOW ))
235 return PR_FALSE;
236 mStatus |= QUOTE;
237 mValue = aValue;
238 return PR_TRUE;
241 /*************** aaSaveTransaction **********************/
242 aaSaveTransaction::aaSaveTransaction(nsISupports *aOuter)
244 nsresult rv;
245 mConnection = do_QueryInterface(aOuter);
246 PRBool isReady;
247 if (! mConnection)
248 return;
249 rv = mConnection->GetConnectionReady(&isReady);
250 if (NS_FAILED(rv))
251 mConnection = nsnull;
254 aaSaveTransaction::~aaSaveTransaction()
258 NS_IMPL_ISUPPORTS1(aaSaveTransaction,
259 aaISaveQuery)
260 /* aaISaveQuery */
261 NS_IMETHODIMP
262 aaSaveTransaction::Save(aaIDataNode *aNode, aaIDataNode *aOldNode)
264 nsresult rv;
265 NS_ENSURE_TRUE(! aOldNode, NS_ERROR_INVALID_ARG);
267 nsCOMPtr<aaITransaction> txn = do_QueryInterface(aNode);
268 NS_ENSURE_TRUE(txn, NS_ERROR_INVALID_ARG);
270 txn->GetFact(getter_AddRefs( mFact ));
271 NS_ENSURE_TRUE(mFact, NS_ERROR_INVALID_ARG);
273 rv = mConnection->BeginTransaction();
274 NS_ENSURE_SUCCESS(rv, rv);
277 aaGuard guard(mConnection);
279 rv = readRates();
280 NS_ENSURE_SUCCESS(rv, rv);
282 rv = readBalances();
283 NS_ENSURE_SUCCESS(rv, rv);
285 rv = adjustBalances();
286 NS_ENSURE_SUCCESS(rv, rv);
288 rv = saveTxn();
289 NS_ENSURE_SUCCESS(rv, rv);
291 return guard.Dismiss();
295 /* Private methods */
296 nsresult
297 aaSaveTransaction::readRates()
299 nsresult rv;
300 nsCOMPtr<mozIStorageStatement> statement;
302 PRInt64 eventId, id;
303 rv = mFact->GetEventId(&eventId);
304 NS_ENSURE_SUCCESS(rv, rv);
305 rv = mFact->GetId(&id);
306 NS_ENSURE_SUCCESS(rv, rv);
308 PRTime time;
309 rv = mFact->GetTime(&time);
310 NS_ENSURE_SUCCESS(rv, rv);
311 time /= 1000000;
313 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_READ_RATES),
314 getter_AddRefs(statement));
315 NS_ENSURE_SUCCESS(rv, rv);
317 rv = statement->BindInt64Parameter(0, time);
318 NS_ENSURE_SUCCESS(rv, rv);
320 rv = statement->BindInt64Parameter(1, eventId);
321 NS_ENSURE_SUCCESS(rv, rv);
323 rv = statement->BindInt64Parameter(2, id);
324 NS_ENSURE_SUCCESS(rv, rv);
326 PRBool hasMore;
327 PRInt32 state;
328 do {
329 rv = statement->ExecuteStep(&hasMore);
330 if (NS_FAILED(rv))
331 break;
332 rv = statement->GetState(&state);
333 if (NS_FAILED(rv))
334 break;
335 if (state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING) {
336 if ( ! mQuotes.AppendElement(statement) ) {
337 rv = NS_ERROR_OUT_OF_MEMORY;
338 break;
341 } while (hasMore);
342 statement->Reset();
343 NS_ENSURE_SUCCESS(rv, rv);
345 return rv;
348 nsresult
349 aaSaveTransaction::readBalances()
351 nsresult rv = NS_OK;
352 nsCOMPtr<mozIStorageStatement> statement;
354 PRInt64 eventId, id;
355 rv = mFact->GetEventId(&eventId);
356 NS_ENSURE_SUCCESS(rv, rv);
357 rv = mFact->GetId(&id);
358 NS_ENSURE_SUCCESS(rv, rv);
360 PRTime time;
361 rv = mFact->GetTime(&time);
362 NS_ENSURE_SUCCESS(rv, rv);
363 time /= 1000000;
365 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_READ_BALANCES),
366 getter_AddRefs(statement));
367 NS_ENSURE_SUCCESS(rv, rv);
369 rv = statement->BindInt64Parameter(0, time);
370 NS_ENSURE_SUCCESS(rv, rv);
372 rv = statement->BindInt64Parameter(1, eventId);
373 NS_ENSURE_SUCCESS(rv, rv);
375 rv = statement->BindInt64Parameter(2, id);
376 NS_ENSURE_SUCCESS(rv, rv);
378 PRBool hasMore;
379 PRInt32 state;
380 do {
381 rv = statement->ExecuteStep(&hasMore);
382 if (NS_FAILED(rv))
383 break;
384 rv = statement->GetState(&state);
385 if (NS_FAILED(rv))
386 break;
387 if (state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING) {
388 if ( ! mBalances.AppendElement(statement) ) {
389 rv = NS_ERROR_OUT_OF_MEMORY;
390 break;
393 } while (hasMore);
394 statement->Reset();
395 NS_ENSURE_SUCCESS(rv, rv);
397 return rv;
400 nsresult
401 aaSaveTransaction::adjustBalances()
403 PRBool res;
405 aaBalanceRow from, to;
406 res = from.SetSide(mQuotes[2]);
407 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
408 res = from.SetFact(mFact);
409 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
410 res = to.SetSide(mQuotes[1]);
411 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
412 res = to.SetFact(mFact);
413 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
415 NS_ENSURE_TRUE( mBalances.Length() < 2, NS_ERROR_NOT_IMPLEMENTED);
417 if (mBalances.Length()
418 && mBalances[0].GetFactSide() == mBalances[0].GetSide()) {
419 mValue = mBalances[0].GetValue();
420 mBalances[0].Merge(from);
421 mValue -= from.GetValue();
422 to.SetValue(mValue);
423 } else {
424 PRUint32 defQuote = chooseDefaultQuote();
425 NS_ENSURE_TRUE( mQuotes[defQuote].IsReady(), NS_ERROR_UNEXPECTED);
426 mFact->GetAmount( &mValue );
427 mValue *= mQuotes[defQuote].GetQuote();
428 res = from.SetQuote(mQuotes[defQuote]);
429 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
430 res = to.SetQuote(mQuotes[defQuote]);
431 NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG);
434 if ( mBalances.Length() && mBalances[0].GetFactSide() == 0)
435 mBalances[0].Merge(to);
437 if ( from.isSignificant() )
438 mBalances.AppendElement(from);
440 if ( to.isSignificant() )
441 mBalances.AppendElement(to);
442 return NS_OK;
445 nsresult
446 aaSaveTransaction::saveTxn()
448 nsresult rv;
449 nsCOMPtr<mozIStorageStatement> statement;
451 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_SAVE_TXN),
452 getter_AddRefs(statement));
453 NS_ENSURE_SUCCESS(rv, rv);
455 PRTime time;
456 PRInt64 eventId, factId;
458 mFact->GetTime( &time );
459 mFact->GetEventId( &eventId );
460 mFact->GetId ( &factId );
462 rv = statement->BindInt64Parameter(0, time/1000000);
463 NS_ENSURE_SUCCESS(rv, rv);
465 rv = statement->BindInt64Parameter(1, eventId);
466 NS_ENSURE_SUCCESS(rv, rv);
468 rv = statement->BindInt64Parameter(2, factId);
469 NS_ENSURE_SUCCESS(rv, rv);
471 rv = statement->BindDoubleParameter(3, mValue);
472 NS_ENSURE_SUCCESS(rv, rv);
474 rv = statement->Execute();
475 NS_ENSURE_SUCCESS(rv, rv);
477 PRUint32 i = 0;
478 for (; i < mBalances.Length(); i++) {
479 NS_ENSURE_TRUE(mBalances[i].GetStatus() & aaBalanceRow::QUOTE,\
480 NS_ERROR_NOT_INITIALIZED);
481 if (mBalances[i].GetStatus() & aaBalanceRow::UPDATE)
482 updateBalance(mBalances[i]);
483 else if (mBalances[i].GetStatus() & aaBalanceRow::CLEARED)
484 clearBalance(mBalances[i]);
485 else
486 insertBalance(mBalances[i]);
488 return NS_OK;
491 nsresult
492 aaSaveTransaction::insertBalance(const aaBalanceRow& balance)
494 nsresult rv;
495 nsCOMPtr<mozIStorageStatement> statement;
497 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_SAVE_BALANCE),
498 getter_AddRefs(statement));
499 NS_ENSURE_SUCCESS(rv, rv);
501 rv = statement->BindInt64Parameter(0, balance.GetFlowId());
502 NS_ENSURE_SUCCESS(rv, rv);
504 rv = statement->BindInt32Parameter(1, balance.GetSide());
505 NS_ENSURE_SUCCESS(rv, rv);
507 rv = statement->BindDoubleParameter(2, balance.GetAmount());
508 NS_ENSURE_SUCCESS(rv, rv);
510 rv = statement->BindDoubleParameter(3, balance.GetValue());
511 NS_ENSURE_SUCCESS(rv, rv);
513 rv = statement->BindInt64Parameter(4, balance.GetStart()/1000000);
514 NS_ENSURE_SUCCESS(rv, rv);
516 return statement->Execute();
519 nsresult
520 aaSaveTransaction::updateBalance(const aaBalanceRow& balance)
522 nsresult rv;
523 nsCOMPtr<mozIStorageStatement> statement;
525 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_BALANCE_UPDATE),
526 getter_AddRefs(statement));
527 NS_ENSURE_SUCCESS(rv, rv);
529 rv = statement->BindInt64Parameter(3, balance.GetId());
530 NS_ENSURE_SUCCESS(rv, rv);
532 rv = statement->BindInt32Parameter(0, balance.GetSide());
533 NS_ENSURE_SUCCESS(rv, rv);
535 rv = statement->BindDoubleParameter(1, balance.GetAmount());
536 NS_ENSURE_SUCCESS(rv, rv);
538 rv = statement->BindDoubleParameter(2, balance.GetValue());
539 NS_ENSURE_SUCCESS(rv, rv);
541 return statement->Execute();
544 nsresult
545 aaSaveTransaction::clearBalance(const aaBalanceRow& balance)
547 nsresult rv;
548 nsCOMPtr<mozIStorageStatement> statement;
550 rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_BALANCE_CLEAR),
551 getter_AddRefs(statement));
552 NS_ENSURE_SUCCESS(rv, rv);
554 rv = statement->BindInt64Parameter(1, balance.GetId());
555 NS_ENSURE_SUCCESS(rv, rv);
557 rv = statement->BindInt64Parameter(0, balance.GetPaid()/1000000);
558 NS_ENSURE_SUCCESS(rv, rv);
560 return statement->Execute();