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: */
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>
26 #include "nsStringAPI.h"
27 #include "nsEmbedString.h"
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"
41 #define AA_READ_RATES "SELECT fact.side, term.side, flow.rate, quote.rate\
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\
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',\
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 = ?\
76 #define AA_BALANCE_CLEAR "UPDATE balance\
77 SET paid = replace(round(julianday(?,'unixepoch')),'.0','')\
80 /****************** aaFullQuote *************************/
81 aaFullQuote::aaFullQuote(mozIStorageStatement
*aStatement
)
82 :mReady(PR_FALSE
), mType(3)
87 rv
= aStatement
->GetInt32( 0, &side1
);
90 rv
= aStatement
->GetInt32( 1, &side2
);
93 rv
= aStatement
->GetDouble( 2, &mRate
);
96 rv
= aStatement
->GetIsNull( 3, &isNull
);
105 rv
= aStatement
->GetDouble( 3, &mQuote
);
111 /***************** aaBalanceRow *************************/
112 aaBalanceRow::aaBalanceRow(const aaBalanceRow
& other
)
114 memcpy((void *) this, (const void *) &other
, sizeof(aaBalanceRow
));
117 aaBalanceRow::aaBalanceRow(mozIStorageStatement
*aStatement
)
121 rv
= aStatement
->GetInt32( 0, &mFactSide
);
124 rv
= aStatement
->GetInt64( 1, &mId
);
127 rv
= aStatement
->GetInt64( 2, &mFlowId
);
130 rv
= aStatement
->GetDouble( 3, &mFlowRate
);
133 rv
= aStatement
->GetInt32( 4, &mSide
);
136 rv
= aStatement
->GetDouble( 5, &mAmount
);
139 rv
= aStatement
->GetDouble( 6, &mValue
);
142 rv
= aStatement
->GetInt64( 7, &mStart
);
147 mStatus
= SIDE
| FLOW
| QUOTE
;
151 aaBalanceRow::SetSide(const aaFullQuote
& aQuote
)
154 if (NS_UNLIKELY( ! aQuote
.GetType() ))
156 rate
= aQuote
.GetRate();
160 mSide
= aQuote
.GetType() & 1;
167 aaBalanceRow::SetFact(aaIFact
*aFact
)
169 if (NS_UNLIKELY( mStatus
!= SIDE
))
171 mStatus
= FLOW
| SIDE
;
172 nsCOMPtr
<aaIFlow
> flow
;
174 aFact
->GetTakeFrom(getter_AddRefs( flow
));
176 aFact
->GetGiveTo(getter_AddRefs( flow
));
178 flow
->GetId( &mFlowId
);
179 aFact
->GetAmount( &mAmount
);
180 mAmount
*= mFlowRate
;
181 aFact
->GetTime( &mStart
);
187 aaBalanceRow::Merge(aaBalanceRow
& other
)
189 if (other
.GetStart() == mStart
) {
194 other
.Consume( *this );
195 mPaid
= other
.GetStart();
201 aaBalanceRow::Consume(aaBalanceRow
& other
)
203 if (other
.GetSide() == mSide
) {
204 mAmount
+= other
.GetAmount();
205 mValue
+= other
.GetValue();
208 mAmount
= other
.GetAmount() * mFlowRate
- mAmount
;
209 mValue
= mAmount
* other
.GetValue() / other
.GetAmount();
215 aaBalanceRow::Flash()
222 aaBalanceRow::SetQuote(const aaFullQuote
& aQuote
)
224 if (NS_UNLIKELY( ! mStatus
& FLOW
|| ! aQuote
.IsReady() ))
227 mValue
= mAmount
* aQuote
.GetQuote() / mFlowRate
;
232 aaBalanceRow::SetValue(const double aValue
)
234 if (NS_UNLIKELY( ! mStatus
& FLOW
))
241 /*************** aaSaveTransaction **********************/
242 aaSaveTransaction::aaSaveTransaction(nsISupports
*aOuter
)
245 mConnection
= do_QueryInterface(aOuter
);
249 rv
= mConnection
->GetConnectionReady(&isReady
);
251 mConnection
= nsnull
;
254 aaSaveTransaction::~aaSaveTransaction()
258 NS_IMPL_ISUPPORTS1(aaSaveTransaction
,
262 aaSaveTransaction::Save(aaIDataNode
*aNode
, aaIDataNode
*aOldNode
)
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
);
280 NS_ENSURE_SUCCESS(rv
, rv
);
283 NS_ENSURE_SUCCESS(rv
, rv
);
285 rv
= adjustBalances();
286 NS_ENSURE_SUCCESS(rv
, rv
);
289 NS_ENSURE_SUCCESS(rv
, rv
);
291 return guard
.Dismiss();
295 /* Private methods */
297 aaSaveTransaction::readRates()
300 nsCOMPtr
<mozIStorageStatement
> statement
;
303 rv
= mFact
->GetEventId(&eventId
);
304 NS_ENSURE_SUCCESS(rv
, rv
);
305 rv
= mFact
->GetId(&id
);
306 NS_ENSURE_SUCCESS(rv
, rv
);
309 rv
= mFact
->GetTime(&time
);
310 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
329 rv
= statement
->ExecuteStep(&hasMore
);
332 rv
= statement
->GetState(&state
);
335 if (state
== mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING
) {
336 if ( ! mQuotes
.AppendElement(statement
) ) {
337 rv
= NS_ERROR_OUT_OF_MEMORY
;
343 NS_ENSURE_SUCCESS(rv
, rv
);
349 aaSaveTransaction::readBalances()
352 nsCOMPtr
<mozIStorageStatement
> statement
;
355 rv
= mFact
->GetEventId(&eventId
);
356 NS_ENSURE_SUCCESS(rv
, rv
);
357 rv
= mFact
->GetId(&id
);
358 NS_ENSURE_SUCCESS(rv
, rv
);
361 rv
= mFact
->GetTime(&time
);
362 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
381 rv
= statement
->ExecuteStep(&hasMore
);
384 rv
= statement
->GetState(&state
);
387 if (state
== mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING
) {
388 if ( ! mBalances
.AppendElement(statement
) ) {
389 rv
= NS_ERROR_OUT_OF_MEMORY
;
395 NS_ENSURE_SUCCESS(rv
, rv
);
401 aaSaveTransaction::adjustBalances()
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();
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
);
446 aaSaveTransaction::saveTxn()
449 nsCOMPtr
<mozIStorageStatement
> statement
;
451 rv
= mConnection
->CreateStatement(NS_LITERAL_CSTRING(AA_SAVE_TXN
),
452 getter_AddRefs(statement
));
453 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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
]);
486 insertBalance(mBalances
[i
]);
492 aaSaveTransaction::insertBalance(const aaBalanceRow
& balance
)
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();
520 aaSaveTransaction::updateBalance(const aaBalanceRow
& balance
)
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();
545 aaSaveTransaction::clearBalance(const aaBalanceRow
& balance
)
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();