forked from Qortal/qortal
Fix deleting rows from HSQLDB repository & improve transaction tests
This commit is contained in:
parent
fe6cb4e366
commit
70d25f24ce
@ -1,5 +1,7 @@
|
||||
package repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import data.voting.PollData;
|
||||
import data.voting.VoteOnPollData;
|
||||
|
||||
@ -17,6 +19,8 @@ public interface VotingRepository {
|
||||
|
||||
// Votes
|
||||
|
||||
public List<VoteOnPollData> getVotes(String pollName) throws DataException;
|
||||
|
||||
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException;
|
||||
|
||||
public void save(VoteOnPollData voteOnPollData) throws DataException;
|
||||
|
@ -68,7 +68,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
|
||||
public void delete(String address, long assetId) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM AccountBalances WHERE account = ? and asset_id = ?", address, assetId);
|
||||
this.repository.delete("AccountBalances", "account = ? and asset_id = ?", address, assetId);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete account balance from repository", e);
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
|
||||
public void delete(long assetId) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM Assets WHERE assetId = ?", assetId);
|
||||
this.repository.delete("Assets", "assetId = ?", assetId);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete asset from repository", e);
|
||||
}
|
||||
@ -119,7 +119,7 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
|
||||
public void delete(byte[] orderId) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM AssetOrders WHERE orderId = ?", orderId);
|
||||
this.repository.delete("AssetOrders", "orderId = ?", orderId);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete asset order from repository", e);
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
|
||||
public void delete(BlockData blockData) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM Blocks WHERE signature = ?", blockData.getSignature());
|
||||
this.repository.delete("Blocks", "signature = ?", blockData.getSignature());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete Block from repository", e);
|
||||
}
|
||||
@ -166,7 +166,7 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
|
||||
public void delete(BlockTransactionData blockTransactionData) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM BlockTransactions WHERE block_signature = ? AND sequence = ? AND transaction_signature = ?",
|
||||
this.repository.delete("BlockTransactions", "block_signature = ? AND sequence = ? AND transaction_signature = ?",
|
||||
blockTransactionData.getBlockSignature(), blockTransactionData.getSequence(), blockTransactionData.getTransactionSignature());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete BlockTransaction from repository", e);
|
||||
|
@ -112,7 +112,26 @@ public class HSQLDBRepository implements Repository {
|
||||
public ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||
PreparedStatement preparedStatement = this.connection.prepareStatement(sql);
|
||||
|
||||
return this.checkedExecute(preparedStatement, objects);
|
||||
return this.checkedExecuteResultSet(preparedStatement, objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind objects to placeholders in prepared statement.
|
||||
* <p>
|
||||
* Special treatment for BigDecimals so that they retain their "scale".
|
||||
*
|
||||
* @param preparedStatement
|
||||
* @param objects
|
||||
* @throws SQLException
|
||||
*/
|
||||
private void prepareExecute(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
||||
for (int i = 0; i < objects.length; ++i)
|
||||
// Special treatment for BigDecimals so that they retain their "scale",
|
||||
// which would otherwise be assumed as 0.
|
||||
if (objects[i] instanceof BigDecimal)
|
||||
preparedStatement.setBigDecimal(i + 1, (BigDecimal) objects[i]);
|
||||
else
|
||||
preparedStatement.setObject(i + 1, objects[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,14 +144,8 @@ public class HSQLDBRepository implements Repository {
|
||||
* @return ResultSet, or null if there are no found rows
|
||||
* @throws SQLException
|
||||
*/
|
||||
public ResultSet checkedExecute(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
||||
for (int i = 0; i < objects.length; ++i)
|
||||
// Special treatment for BigDecimals so that they retain their "scale",
|
||||
// which would otherwise be assumed as 0.
|
||||
if (objects[i] instanceof BigDecimal)
|
||||
preparedStatement.setBigDecimal(i + 1, (BigDecimal) objects[i]);
|
||||
else
|
||||
preparedStatement.setObject(i + 1, objects[i]);
|
||||
private ResultSet checkedExecuteResultSet(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
||||
prepareExecute(preparedStatement, objects);
|
||||
|
||||
if (!preparedStatement.execute())
|
||||
throw new SQLException("Fetching from database produced no results");
|
||||
@ -147,6 +160,27 @@ public class HSQLDBRepository implements Repository {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute PreparedStatement and return changed row count.
|
||||
*
|
||||
* @param preparedStatement
|
||||
* @param objects
|
||||
* @return number of changed rows
|
||||
* @throws SQLException
|
||||
*/
|
||||
private int checkedExecuteUpdateCount(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
||||
prepareExecute(preparedStatement, objects);
|
||||
|
||||
if (preparedStatement.execute())
|
||||
throw new SQLException("Database produced results, not row count");
|
||||
|
||||
int rowCount = preparedStatement.getUpdateCount();
|
||||
if (rowCount == -1)
|
||||
throw new SQLException("Database returned invalid row count");
|
||||
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch last value of IDENTITY column after an INSERT statement.
|
||||
* <p>
|
||||
@ -159,7 +193,7 @@ public class HSQLDBRepository implements Repository {
|
||||
*/
|
||||
public Long callIdentity() throws SQLException {
|
||||
PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
|
||||
ResultSet resultSet = this.checkedExecute(preparedStatement);
|
||||
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement);
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
@ -185,11 +219,24 @@ public class HSQLDBRepository implements Repository {
|
||||
*/
|
||||
public boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||
PreparedStatement preparedStatement = this.connection.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " LIMIT 1");
|
||||
ResultSet resultSet = this.checkedExecute(preparedStatement, objects);
|
||||
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement, objects);
|
||||
if (resultSet == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete rows from database table.
|
||||
*
|
||||
* @param tableName
|
||||
* @param whereClause
|
||||
* @param objects
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void delete(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||
PreparedStatement preparedStatement = this.connection.prepareStatement("DELETE FROM " + tableName + " WHERE " + whereClause);
|
||||
this.checkedExecuteUpdateCount(preparedStatement, objects);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
// Votes
|
||||
// Polls
|
||||
|
||||
public PollData fromPollName(String pollName) throws DataException {
|
||||
try {
|
||||
@ -93,7 +93,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
||||
// NOTE: The corresponding rows in PollOptions are deleted automatically by the database thanks to "ON DELETE CASCADE" in the PollOptions' FOREIGN KEY
|
||||
// definition.
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM Polls WHERE poll_name = ?", pollName);
|
||||
this.repository.delete("Polls", "poll_name = ?", pollName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete poll from repository", e);
|
||||
}
|
||||
@ -101,6 +101,28 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
||||
|
||||
// Votes
|
||||
|
||||
public List<VoteOnPollData> getVotes(String pollName) throws DataException {
|
||||
List<VoteOnPollData> votes = new ArrayList<VoteOnPollData>();
|
||||
|
||||
try {
|
||||
ResultSet resultSet = this.repository.checkedExecute("SELECT voter, option_index FROM PollVotes WHERE poll_name = ?", pollName);
|
||||
if (resultSet == null)
|
||||
return votes;
|
||||
|
||||
// NOTE: do-while because checkedExecute() above has already called rs.next() for us
|
||||
do {
|
||||
byte[] voterPublicKey = resultSet.getBytes(1);
|
||||
int optionIndex = resultSet.getInt(2);
|
||||
|
||||
votes.add(new VoteOnPollData(pollName, voterPublicKey, optionIndex));
|
||||
} while (resultSet.next());
|
||||
|
||||
return votes;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch poll votes from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException {
|
||||
try {
|
||||
ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
|
||||
@ -131,7 +153,7 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
||||
|
||||
public void delete(String pollName, byte[] voterPublicKey) throws DataException {
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName, voterPublicKey);
|
||||
this.repository.delete("PollVotes", "poll_name = ? AND voter = ?", pollName, voterPublicKey);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete poll vote from repository", e);
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
// NOTE: The corresponding row in sub-table is deleted automatically by the database thanks to "ON DELETE CASCADE" in the sub-table's FOREIGN KEY
|
||||
// definition.
|
||||
try {
|
||||
this.repository.checkedExecute("DELETE FROM Transactions WHERE signature = ?", transactionData.getSignature());
|
||||
this.repository.delete("Transactions", "signature = ?", transactionData.getSignature());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete transaction from repository", e);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import data.transaction.PaymentTransactionData;
|
||||
import data.transaction.VoteOnPollTransactionData;
|
||||
import data.voting.PollData;
|
||||
import data.voting.PollOptionData;
|
||||
import data.voting.VoteOnPollData;
|
||||
import qora.account.Account;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.account.PublicKeyAccount;
|
||||
@ -245,11 +246,40 @@ public class TransactionTests {
|
||||
block.process();
|
||||
repository.saveChanges();
|
||||
|
||||
// Check vote was registered properly
|
||||
VoteOnPollData actualVoteOnPollData = repository.getVotingRepository().getVote(pollName, sender.getPublicKey());
|
||||
assertNotNull(actualVoteOnPollData);
|
||||
assertEquals(optionIndex, actualVoteOnPollData.getOptionIndex());
|
||||
|
||||
// update variables for next round
|
||||
previousBlockData = block.getBlockData();
|
||||
timestamp += 1_000;
|
||||
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
||||
}
|
||||
|
||||
// Check poll's votes
|
||||
List<VoteOnPollData> votes = repository.getVotingRepository().getVotes(pollName);
|
||||
assertNotNull(votes);
|
||||
|
||||
assertEquals("Only one vote expected", 1, votes.size());
|
||||
|
||||
assertEquals("Wrong vote option index", pollOptionsSize - 1, votes.get(0).getOptionIndex());
|
||||
assertTrue("Wrong voter public key", Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()));
|
||||
|
||||
// Orphan last block
|
||||
BlockData lastBlockData = repository.getBlockRepository().getLastBlock();
|
||||
Block lastBlock = new Block(repository, lastBlockData);
|
||||
lastBlock.orphan();
|
||||
repository.saveChanges();
|
||||
|
||||
// Recheck poll's votes
|
||||
votes = repository.getVotingRepository().getVotes(pollName);
|
||||
assertNotNull(votes);
|
||||
|
||||
assertEquals("Only one vote expected", 1, votes.size());
|
||||
|
||||
assertEquals("Wrong vote option index", pollOptionsSize - 1 - 1, votes.get(0).getOptionIndex());
|
||||
assertTrue("Wrong voter public key", Arrays.equals(sender.getPublicKey(), votes.get(0).getVoterPublicKey()));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user