From fceeeac571d892a990733bd6d43cfa73551e1331 Mon Sep 17 00:00:00 2001 From: Sergey Yanovich Date: Sat, 20 Oct 2007 01:02:43 +0300 Subject: [PATCH] [storage] Process same day balance increase --- storage/aaAccountTest.cpp | 64 +++++++++++- storage/aaSaveTransaction.cpp | 237 +++++++++++++++++++++++++++++++++++------- storage/aaSaveTransaction.h | 33 ++++-- 3 files changed, 287 insertions(+), 47 deletions(-) diff --git a/storage/aaAccountTest.cpp b/storage/aaAccountTest.cpp index 14668f9..a67231b 100644 --- a/storage/aaAccountTest.cpp +++ b/storage/aaAccountTest.cpp @@ -74,6 +74,7 @@ private: nsresult testPendingFacts(nsITestRunner *aTestRunner); nsresult testQuote(nsITestRunner *aTestRunner); nsresult testTransaction(nsITestRunner *aTestRunner); + nsresult testUpdateTransaction(nsITestRunner *aTestRunner); }; NS_IMPL_ISUPPORTS1(aaAccountTest, nsITest); @@ -103,6 +104,7 @@ aaAccountTest::Test(nsITestRunner *aTestRunner) testPendingFacts(aTestRunner); testQuote(aTestRunner); testTransaction(aTestRunner); + testUpdateTransaction(aTestRunner); return NS_OK; } @@ -183,7 +185,6 @@ testBalance(nsITestRunner *cxxUnitTestRunner, aaIBalance *node, PRInt64 flowId, if (NS_UNLIKELY( id != objectId) ) { equals = PR_FALSE; NS_TEST_ASSERT_MSG(PR_FALSE, " 'balance.object.id' is wrong"); - printf_stderr("%u\n", id); } } else { equals = PR_FALSE; @@ -294,7 +295,6 @@ aaAccountTest::testPendingFacts(nsITestRunner *aTestRunner) NS_TEST_ASSERT_MSG(count == 3, "[pending facts] wrong flow count"); mPendingFact = do_QueryElementAt(set, 0, &rv); - NS_TEST_ASSERT_MSG(NS_SUCCEEDED(rv), "[pending facts] quering 1st fact"); NS_TEST_ASSERT_MSG(mPendingFact, "[pending facts] 1st fact not loaded" ); NS_TEST_ASSERT_MSG(testFact(aTestRunner, mPendingFact, 2, 3, @@ -415,6 +415,66 @@ aaAccountTest::testTransaction(nsITestRunner *aTestRunner) set->GetLength(&count); NS_TEST_ASSERT_MSG(count == 2, "[transaction] wrong fact count"); + mPendingFact = do_QueryElementAt(set, 0, &rv); + NS_TEST_ASSERT_MSG(mPendingFact, "[transaction] pending fact not loaded" ); + + NS_TEST_ASSERT_MSG(testFact(aTestRunner, mPendingFact, 1, 3, + AA_EVENT_AMOUNT_3), "[transaction] pending fact is wrong"); + return NS_OK; +} + +nsresult +aaAccountTest::testUpdateTransaction(nsITestRunner *aTestRunner) +{ + nsresult rv; + NS_TEST_BEGIN(aTestRunner); + + nsCOMPtr node(do_CreateInstance( + "@aasii.org/base/transaction;1", &rv)); + NS_TEST_ASSERT_MSG(node, "[update txn] instance not created" ); + NS_ENSURE_TRUE(node, rv); + + node->SetFact(mPendingFact); + + rv = mSession->Save(node, nsnull); + NS_TEST_ASSERT_MSG(NS_SUCCEEDED(rv), "[update txn] saving" ); + + nsCOMPtr set; + rv = mSession->Load(AA_LOADBALANCE_CONTRACT_ID, getter_AddRefs( set )); + NS_TEST_ASSERT_MSG(set, "[update txn] result set not loaded" ); + NS_ENSURE_TRUE(set, rv); + + PRUint32 count; + set->GetLength(&count); + NS_TEST_ASSERT_MSG(count == 3, "[update txn] wrong flow count"); + + nsCOMPtr balance(do_QueryElementAt(set, 0)); + NS_TEST_ASSERT_MSG(testBalance(aTestRunner, balance, 2, 2, AA_EVENT_AMOUNT_2 + / AA_FLOW_SHARE_RATE, AA_EVENT_AMOUNT_2), + "[update txn] flow2 is wrong"); + + balance = do_QueryElementAt(set, 1); + NS_TEST_ASSERT_MSG(testBalance(aTestRunner, balance, 3, 1, AA_EVENT_AMOUNT_2 + + AA_EVENT_AMOUNT_3, AA_EVENT_AMOUNT_2 + AA_EVENT_AMOUNT_3), + "[update txn] flow3 is wrong"); + + balance = do_QueryElementAt(set, 2); + NS_TEST_ASSERT_MSG(testBalance(aTestRunner, balance, 1, 2, AA_EVENT_AMOUNT_3 + / AA_FLOW_SHARE_RATE, AA_EVENT_AMOUNT_3), + "[update txn] flow1 is wrong"); + + rv = mSession->Load(AA_LOADPENDINGFACTS_CONTRACT_ID, getter_AddRefs( set )); + NS_TEST_ASSERT_MSG(set, "[update txn] fact set not loaded" ); + NS_ENSURE_TRUE(set, rv); + + set->GetLength(&count); + NS_TEST_ASSERT_MSG(count == 1, "[update txn] wrong fact count"); + + mPendingFact = do_QueryElementAt(set, 0, &rv); + NS_TEST_ASSERT_MSG(mPendingFact, "[update txn] pending fact not loaded" ); + + NS_TEST_ASSERT_MSG(testFact(aTestRunner, mPendingFact, 3, 4, + AA_EVENT_AMOUNT_4), "[update txn] pending fact is wrong"); return NS_OK; } /* Boilerplate - factory & module */ diff --git a/storage/aaSaveTransaction.cpp b/storage/aaSaveTransaction.cpp index b62c18e..2de23d9 100644 --- a/storage/aaSaveTransaction.cpp +++ b/storage/aaSaveTransaction.cpp @@ -20,6 +20,7 @@ */ #include +#include #include "nsCOMPtr.h" #include "nsStringAPI.h" @@ -50,12 +51,28 @@ quote.day=(SELECT MAX(day) FROM quote AS list\ WHERE list.object_id=term.object_id AND list.day<=transfer.day)" +#define AA_READ_BALANCES "SELECT fact.side, balance.id, fact.flow_id,\ + flow.rate, balance.side, balance.amount, balance.value, strftime('%s',\ + balance.start)\ + FROM fact\ + LEFT JOIN transfer ON fact.day == (replace(round(julianday(?,'unixepoch')),\ + '.0', '')) AND fact.event_id == ? AND fact.transfer_id == ? AND\ + fact.day == transfer.day AND fact.event_id == transfer.event_id AND\ + fact.transfer_id == transfer.id\ + LEFT JOIN flow ON fact.flow_id == flow.id\ + LEFT JOIN balance ON fact.flow_id == balance.flow_id AND balance.paid IS NULL\ + WHERE balance.id IS NOT NULL\ + ORDER BY fact.side DESC, balance.start" + #define AA_SAVE_TXN "INSERT INTO txn (day, event_id, transfer_id, value)\ VALUES ((replace(round(julianday(?,'unixepoch')),'.0','')), ?, ?, ?)" #define AA_SAVE_BALANCE "INSERT INTO balance (flow_id, side, amount, value,\ start) VALUES (?,?,?,?,(replace(round(julianday(?,'unixepoch')),'.0','')))" +#define AA_BALANCE_UPDATE "UPDATE balance SET side = ?, amount = ?, value = ?\ + WHERE id = ?" + /****************** aaFullQuote *************************/ aaFullQuote::aaFullQuote(mozIStorageStatement *aStatement) :mReady(PR_FALSE), mType(3) @@ -88,6 +105,44 @@ aaFullQuote::aaFullQuote(mozIStorageStatement *aStatement) } /***************** aaBalanceRow *************************/ +aaBalanceRow::aaBalanceRow(const aaBalanceRow& other) +{ + memcpy((void *) this, (const void *) &other, sizeof(aaBalanceRow)); +} + +aaBalanceRow::aaBalanceRow(mozIStorageStatement *aStatement) + :mStatus(0) +{ + nsresult rv; + rv = aStatement->GetInt32( 0, &mFactSide ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetInt64( 1, &mId ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetInt64( 2, &mFlowId ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetDouble( 3, &mFlowRate ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetInt32( 4, &mSide ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetDouble( 5, &mAmount ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetDouble( 6, &mValue ); + if (NS_FAILED( rv )) + return; + rv = aStatement->GetInt64( 7, &mStart ); + if (NS_FAILED( rv )) + return; + mStart *= 1000000; + mPaid = 0; + mStatus = SIDE | FLOW | QUOTE; +} + PRBool aaBalanceRow::SetSide(const aaFullQuote& aQuote) { @@ -99,16 +154,17 @@ aaBalanceRow::SetSide(const aaFullQuote& aQuote) return PR_FALSE; mFlowRate = rate; mSide = aQuote.GetType() & 1; - mStatus = side; + mFactSide = ! mSide; + mStatus = SIDE; return PR_TRUE; } PRBool aaBalanceRow::SetFact(aaIFact *aFact) { - if (NS_UNLIKELY( ! mStatus & side )) + if (NS_UNLIKELY( mStatus != SIDE )) return PR_FALSE; - mStatus = flow | side; + mStatus = FLOW | SIDE; nsCOMPtr flow; if ( ! mSide ) { aFact->GetTakeFrom(getter_AddRefs( flow )); @@ -117,17 +173,44 @@ aaBalanceRow::SetFact(aaIFact *aFact) } flow->GetId( &mFlowId ); aFact->GetAmount( &mAmount ); + mAmount *= mFlowRate; aFact->GetTime( &mStart ); + mPaid = 0; return PR_TRUE; } +void +aaBalanceRow::Merge(aaBalanceRow& other) +{ + if (other.GetStart() == mStart) + Consume( other ); +} + +void +aaBalanceRow::Consume(aaBalanceRow& other) +{ + if (other.GetSide() == mSide) { + mAmount += other.GetAmount(); + mValue += other.GetValue(); + } + other.Flash(); + mStatus |= UPDATE; +} + +void +aaBalanceRow::Flash() +{ + mAmount = 0.0; + mValue = 0.0; +} + PRBool aaBalanceRow::SetQuote(const aaFullQuote& aQuote) { - if (NS_UNLIKELY( ! mStatus & flow || ! aQuote.IsReady() )) + if (NS_UNLIKELY( ! mStatus & FLOW || ! aQuote.IsReady() )) return PR_FALSE; - mStatus |= quote; - mQuoteRate = aQuote.GetQuote(); + mStatus |= QUOTE; + mValue = mAmount * aQuote.GetQuote() / mFlowRate; return PR_TRUE; } @@ -181,9 +264,6 @@ aaSaveTransaction::Save(aaIDataNode *aNode, aaIDataNode *aOldNode) rv = saveTxn(); NS_ENSURE_SUCCESS(rv, rv); - rv = saveBalances(); - NS_ENSURE_SUCCESS(rv, rv); - return guard.Dismiss(); } } @@ -229,7 +309,10 @@ aaSaveTransaction::readRates() if (NS_FAILED(rv)) break; if (state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING) { - mQuotes.InsertElementAt(mQuotes.Length(), statement); + if ( ! mQuotes.AppendElement(statement) ) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } } } while (hasMore); statement->Reset(); @@ -242,6 +325,50 @@ nsresult aaSaveTransaction::readBalances() { nsresult rv = NS_OK; + nsCOMPtr statement; + + PRInt64 eventId, id; + rv = mFact->GetEventId(&eventId); + NS_ENSURE_SUCCESS(rv, rv); + rv = mFact->GetId(&id); + NS_ENSURE_SUCCESS(rv, rv); + + PRTime time; + rv = mFact->GetTime(&time); + NS_ENSURE_SUCCESS(rv, rv); + time /= 1000000; + + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_READ_BALANCES), + getter_AddRefs(statement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt64Parameter(0, time); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt64Parameter(1, eventId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt64Parameter(2, id); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool hasMore; + PRInt32 state; + do { + rv = statement->ExecuteStep(&hasMore); + if (NS_FAILED(rv)) + break; + rv = statement->GetState(&state); + if (NS_FAILED(rv)) + break; + if (state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_EXECUTING) { + if ( ! mBalances.AppendElement(statement) ) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + } while (hasMore); + statement->Reset(); + NS_ENSURE_SUCCESS(rv, rv); return rv; } @@ -254,19 +381,32 @@ aaSaveTransaction::adjustBalances() mFact->GetAmount( &mValue ); mValue *= mQuotes[0].GetQuote(); - NS_ENSURE_TRUE( mBalances.AppendElements(2), NS_ERROR_OUT_OF_MEMORY); - res = mBalances[0].SetSide(mQuotes[2]); + aaBalanceRow from, to; + res = from.SetSide(mQuotes[2]); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); - res = mBalances[0].SetFact(mFact); + res = from.SetFact(mFact); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); - res = mBalances[0].SetQuote(mQuotes[0]); + res = from.SetQuote(mQuotes[0]); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); - res = mBalances[1].SetSide(mQuotes[1]); + res = to.SetSide(mQuotes[1]); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); - res = mBalances[1].SetFact(mFact); + res = to.SetFact(mFact); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); - res = mBalances[1].SetQuote(mQuotes[0]); + res = to.SetQuote(mQuotes[0]); NS_ENSURE_TRUE( res, NS_ERROR_INVALID_ARG); + + PRUint32 i = 0; + for (; i < mBalances.Length() && mBalances[i].GetFactSide() == 1; i++) + mBalances[i].Merge(from); + + for (; i < mBalances.Length() && mBalances[i].GetFactSide() == 0; i++) + mBalances[i].Merge(to); + + if ( from.isSignificant() ) + mBalances.AppendElement(from); + + if ( to.isSignificant() ) + mBalances.AppendElement(to); return NS_OK; } @@ -302,11 +442,20 @@ aaSaveTransaction::saveTxn() rv = statement->Execute(); NS_ENSURE_SUCCESS(rv, rv); + PRUint32 i = 0; + for (; i < mBalances.Length(); i++) { + NS_ENSURE_TRUE(mBalances[i].GetStatus() & aaBalanceRow::QUOTE,\ + NS_ERROR_NOT_INITIALIZED); + if (mBalances[i].GetStatus() & aaBalanceRow::UPDATE) + updateBalance(mBalances[i]); + else + insertBalance(mBalances[i]); + } return NS_OK; } nsresult -aaSaveTransaction::saveBalances() +aaSaveTransaction::insertBalance(const aaBalanceRow& balance) { nsresult rv; nsCOMPtr statement; @@ -315,29 +464,45 @@ aaSaveTransaction::saveBalances() getter_AddRefs(statement)); NS_ENSURE_SUCCESS(rv, rv); - PRUint32 i = 0; - for (; i < mBalances.Length(); i++) { - NS_ENSURE_TRUE(mBalances[i].GetStatus() & aaBalanceRow::quote,\ - NS_ERROR_NOT_INITIALIZED); + rv = statement->BindInt64Parameter(0, balance.GetFlowId()); + NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(0, mBalances[i].GetFlowId()); - NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindInt32Parameter(1, balance.GetSide()); + NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt32Parameter(1, mBalances[i].GetSide()); - NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindDoubleParameter(2, balance.GetAmount()); + NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindDoubleParameter(2, mBalances[i].GetAmount()); - NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindDoubleParameter(3, balance.GetValue()); + NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindDoubleParameter(3, mBalances[i].GetValue()); - NS_ENSURE_SUCCESS(rv, rv); + rv = statement->BindInt64Parameter(4, balance.GetStart()/1000000); + NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindInt64Parameter(4, mBalances[i].GetStart()/1000000); - NS_ENSURE_SUCCESS(rv, rv); + return statement->Execute(); +} - rv = statement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - } +nsresult +aaSaveTransaction::updateBalance(const aaBalanceRow& balance) +{ + nsresult rv; + nsCOMPtr statement; - return NS_OK; + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(AA_BALANCE_UPDATE), + getter_AddRefs(statement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt64Parameter(3, balance.GetId()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt32Parameter(0, balance.GetSide()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindDoubleParameter(1, balance.GetAmount()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindDoubleParameter(2, balance.GetValue()); + NS_ENSURE_SUCCESS(rv, rv); + + return statement->Execute(); } diff --git a/storage/aaSaveTransaction.h b/storage/aaSaveTransaction.h index e91135c..beb5ce4 100644 --- a/storage/aaSaveTransaction.h +++ b/storage/aaSaveTransaction.h @@ -50,33 +50,47 @@ class aaBalanceRow { public: aaBalanceRow() :mStatus(0) {;} - aaBalanceRow(const aaBalanceRow& other) :mStatus(0) {;} + aaBalanceRow(const aaBalanceRow& other); + aaBalanceRow(mozIStorageStatement *aStatement); enum { - side = 1, - flow = 2, - quote = 4 + SIDE = 1, + FLOW = 2, + QUOTE = 4, + UPDATE = 8 }; + PRBool SetSide(const aaFullQuote& aQuote); PRBool SetFact(aaIFact *aFact); PRBool SetQuote(const aaFullQuote& aQuote); + void Merge(aaBalanceRow& other); + void Consume(aaBalanceRow& other); + void Flash(); + PRBool isSignificant() const { return (mAmount > 0.00009) + || (mAmount < -0.00009);} + PRUint32 GetStatus() const { return mStatus; } + PRInt64 GetId() const { return mId; } PRInt64 GetFlowId() const { return mFlowId; } + PRBool GetFactSide() const { return mFactSide; } PRBool GetSide() const { return mSide; } - double GetAmount() const { return mAmount *mFlowRate; } - double GetValue() const { return mAmount * mQuoteRate; } + double GetAmount() const { return mAmount; } + double GetValue() const { return mValue; } PRTime GetStart() const { return mStart; } + PRTime GetPaid() const { return mPaid; } private: PRUint32 mStatus; + PRInt64 mId; PRInt64 mFlowId; + PRBool mFactSide; PRBool mSide; double mAmount; PRTime mStart; - + PRTime mPaid; double mFlowRate; - double mQuoteRate; + double mValue; }; class aaSaveTransaction : public aaISaveQuery @@ -101,7 +115,8 @@ private: nsresult readBalances(); nsresult adjustBalances(); nsresult saveTxn(); - nsresult saveBalances(); + nsresult insertBalance(const aaBalanceRow& balance); + nsresult updateBalance(const aaBalanceRow& balance); }; #endif /* AASAVETRANSACTION_H */ -- 2.11.4.GIT