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;
|
package repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import data.voting.PollData;
|
import data.voting.PollData;
|
||||||
import data.voting.VoteOnPollData;
|
import data.voting.VoteOnPollData;
|
||||||
|
|
||||||
@ -17,6 +19,8 @@ public interface VotingRepository {
|
|||||||
|
|
||||||
// Votes
|
// Votes
|
||||||
|
|
||||||
|
public List<VoteOnPollData> getVotes(String pollName) throws DataException;
|
||||||
|
|
||||||
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException;
|
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException;
|
||||||
|
|
||||||
public void save(VoteOnPollData voteOnPollData) 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 {
|
public void delete(String address, long assetId) throws DataException {
|
||||||
try {
|
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) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete account balance from repository", 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 {
|
public void delete(long assetId) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.checkedExecute("DELETE FROM Assets WHERE assetId = ?", assetId);
|
this.repository.delete("Assets", "assetId = ?", assetId);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete asset from repository", 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 {
|
public void delete(byte[] orderId) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.checkedExecute("DELETE FROM AssetOrders WHERE orderId = ?", orderId);
|
this.repository.delete("AssetOrders", "orderId = ?", orderId);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete asset order from repository", 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 {
|
public void delete(BlockData blockData) throws DataException {
|
||||||
try {
|
try {
|
||||||
this.repository.checkedExecute("DELETE FROM Blocks WHERE signature = ?", blockData.getSignature());
|
this.repository.delete("Blocks", "signature = ?", blockData.getSignature());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete Block from repository", 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 {
|
public void delete(BlockTransactionData blockTransactionData) throws DataException {
|
||||||
try {
|
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());
|
blockTransactionData.getBlockSignature(), blockTransactionData.getSequence(), blockTransactionData.getTransactionSignature());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete BlockTransaction from repository", 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 {
|
public ResultSet checkedExecute(String sql, Object... objects) throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement(sql);
|
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
|
* @return ResultSet, or null if there are no found rows
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public ResultSet checkedExecute(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
private ResultSet checkedExecuteResultSet(PreparedStatement preparedStatement, Object... objects) throws SQLException {
|
||||||
for (int i = 0; i < objects.length; ++i)
|
prepareExecute(preparedStatement, objects);
|
||||||
// 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]);
|
|
||||||
|
|
||||||
if (!preparedStatement.execute())
|
if (!preparedStatement.execute())
|
||||||
throw new SQLException("Fetching from database produced no results");
|
throw new SQLException("Fetching from database produced no results");
|
||||||
@ -147,6 +160,27 @@ public class HSQLDBRepository implements Repository {
|
|||||||
return resultSet;
|
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.
|
* Fetch last value of IDENTITY column after an INSERT statement.
|
||||||
* <p>
|
* <p>
|
||||||
@ -159,7 +193,7 @@ public class HSQLDBRepository implements Repository {
|
|||||||
*/
|
*/
|
||||||
public Long callIdentity() throws SQLException {
|
public Long callIdentity() throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
|
PreparedStatement preparedStatement = this.connection.prepareStatement("CALL IDENTITY()");
|
||||||
ResultSet resultSet = this.checkedExecute(preparedStatement);
|
ResultSet resultSet = this.checkedExecuteResultSet(preparedStatement);
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -185,11 +219,24 @@ public class HSQLDBRepository implements Repository {
|
|||||||
*/
|
*/
|
||||||
public boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
public boolean exists(String tableName, String whereClause, Object... objects) throws SQLException {
|
||||||
PreparedStatement preparedStatement = this.connection.prepareStatement("SELECT TRUE FROM " + tableName + " WHERE " + whereClause + " LIMIT 1");
|
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)
|
if (resultSet == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
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;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Votes
|
// Polls
|
||||||
|
|
||||||
public PollData fromPollName(String pollName) throws DataException {
|
public PollData fromPollName(String pollName) throws DataException {
|
||||||
try {
|
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
|
// NOTE: The corresponding rows in PollOptions are deleted automatically by the database thanks to "ON DELETE CASCADE" in the PollOptions' FOREIGN KEY
|
||||||
// definition.
|
// definition.
|
||||||
try {
|
try {
|
||||||
this.repository.checkedExecute("DELETE FROM Polls WHERE poll_name = ?", pollName);
|
this.repository.delete("Polls", "poll_name = ?", pollName);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete poll from repository", e);
|
throw new DataException("Unable to delete poll from repository", e);
|
||||||
}
|
}
|
||||||
@ -101,6 +101,28 @@ public class HSQLDBVotingRepository implements VotingRepository {
|
|||||||
|
|
||||||
// Votes
|
// 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 {
|
public VoteOnPollData getVote(String pollName, byte[] voterPublicKey) throws DataException {
|
||||||
try {
|
try {
|
||||||
ResultSet resultSet = this.repository.checkedExecute("SELECT option_index FROM PollVotes WHERE poll_name = ? AND voter = ?", pollName,
|
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 {
|
public void delete(String pollName, byte[] voterPublicKey) throws DataException {
|
||||||
try {
|
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) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete poll vote from repository", 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
|
// 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.
|
// definition.
|
||||||
try {
|
try {
|
||||||
this.repository.checkedExecute("DELETE FROM Transactions WHERE signature = ?", transactionData.getSignature());
|
this.repository.delete("Transactions", "signature = ?", transactionData.getSignature());
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to delete transaction from repository", 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.transaction.VoteOnPollTransactionData;
|
||||||
import data.voting.PollData;
|
import data.voting.PollData;
|
||||||
import data.voting.PollOptionData;
|
import data.voting.PollOptionData;
|
||||||
|
import data.voting.VoteOnPollData;
|
||||||
import qora.account.Account;
|
import qora.account.Account;
|
||||||
import qora.account.PrivateKeyAccount;
|
import qora.account.PrivateKeyAccount;
|
||||||
import qora.account.PublicKeyAccount;
|
import qora.account.PublicKeyAccount;
|
||||||
@ -245,11 +246,40 @@ public class TransactionTests {
|
|||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
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
|
// update variables for next round
|
||||||
previousBlockData = block.getBlockData();
|
previousBlockData = block.getBlockData();
|
||||||
timestamp += 1_000;
|
timestamp += 1_000;
|
||||||
reference = voteOnPollTransaction.getTransactionData().getSignature();
|
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…
x
Reference in New Issue
Block a user