Rewrite of fetchAllTransactionsInvolvingName() to avoid having to load all name transactions into memory.

This commit is contained in:
CalDescent 2022-02-04 13:58:05 +00:00
parent 4e59eb8958
commit 6dec65c5d9
4 changed files with 95 additions and 41 deletions

View File

@ -320,46 +320,7 @@ public class NamesDatabaseIntegrityCheck {
} }
public List<TransactionData> fetchAllTransactionsInvolvingName(String name, Repository repository) throws DataException { public List<TransactionData> fetchAllTransactionsInvolvingName(String name, Repository repository) throws DataException {
List<TransactionData> transactions = new ArrayList<>(); return repository.getTransactionRepository().getTransactionsInvolvingName(name, ConfirmationStatus.CONFIRMED);
String reducedName = Unicode.sanitize(name);
// Fetch all the confirmed name-modification transactions
if (this.nameTransactions.isEmpty()) {
this.fetchAllNameTransactions(repository);
}
for (TransactionData transactionData : this.nameTransactions) {
if ((transactionData instanceof RegisterNameTransactionData)) {
RegisterNameTransactionData registerNameTransactionData = (RegisterNameTransactionData) transactionData;
if (Objects.equals(registerNameTransactionData.getName(), name) ||
Objects.equals(registerNameTransactionData.getReducedName(), reducedName)) {
transactions.add(transactionData);
}
}
if ((transactionData instanceof UpdateNameTransactionData)) {
UpdateNameTransactionData updateNameTransactionData = (UpdateNameTransactionData) transactionData;
boolean hasReducedNewName = updateNameTransactionData.getReducedNewName() == null && !updateNameTransactionData.getReducedNewName().isEmpty();
if (Objects.equals(updateNameTransactionData.getName(), name) ||
(hasReducedNewName && Objects.equals(updateNameTransactionData.getReducedNewName(), reducedName)) ||
Objects.equals(updateNameTransactionData.getNewName(), name)) {
transactions.add(transactionData);
}
}
if ((transactionData instanceof BuyNameTransactionData)) {
BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) transactionData;
if (Objects.equals(buyNameTransactionData.getName(), name)) {
transactions.add(transactionData);
}
}
if ((transactionData instanceof SellNameTransactionData)) {
SellNameTransactionData sellNameTransactionData = (SellNameTransactionData) transactionData;
if (Objects.equals(sellNameTransactionData.getName(), name)) {
transactions.add(transactionData);
}
}
}
return transactions;
} }
private TransactionData fetchLatestModificationTransactionInvolvingName(String registeredName, Repository repository) throws DataException { private TransactionData fetchLatestModificationTransactionInvolvingName(String registeredName, Repository repository) throws DataException {

View File

@ -125,6 +125,17 @@ public interface TransactionRepository {
*/ */
public byte[] getLatestAutoUpdateTransaction(TransactionType txType, int txGroupId, Integer service) throws DataException; public byte[] getLatestAutoUpdateTransaction(TransactionType txType, int txGroupId, Integer service) throws DataException;
/**
* Returns signatures for all name-registration related transactions relating to supplied name.
* Note: this does not currently include ARBITRARY data relating to the name.
*
* @param name
* @param confirmationStatus
* @return
* @throws DataException
*/
public List<TransactionData> getTransactionsInvolvingName(String name, ConfirmationStatus confirmationStatus) throws DataException;
/** /**
* Returns list of transactions relating to specific asset ID. * Returns list of transactions relating to specific asset ID.
* *

View File

@ -30,6 +30,7 @@ import org.qortal.repository.hsqldb.HSQLDBSaver;
import org.qortal.transaction.Transaction.ApprovalStatus; import org.qortal.transaction.Transaction.ApprovalStatus;
import org.qortal.transaction.Transaction.TransactionType; import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.utils.Base58; import org.qortal.utils.Base58;
import org.qortal.utils.Unicode;
public class HSQLDBTransactionRepository implements TransactionRepository { public class HSQLDBTransactionRepository implements TransactionRepository {
@ -700,6 +701,88 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
} }
} }
@Override
public List<TransactionData> getTransactionsInvolvingName(String name, ConfirmationStatus confirmationStatus) throws DataException {
TransactionType[] transactionTypes = new TransactionType[] {
REGISTER_NAME, UPDATE_NAME, BUY_NAME, SELL_NAME
}; // TODO: CancelSellNameTransaction?
String reducedName = Unicode.sanitize(name);
StringBuilder sql = new StringBuilder(1024);
List<Object> bindParams = new ArrayList<>();
sql.append("SELECT Transactions.signature FROM Transactions");
for (int ti = 0; ti < transactionTypes.length; ++ti) {
sql.append(" LEFT OUTER JOIN ");
sql.append(transactionTypes[ti].className);
sql.append("Transactions USING (signature)");
}
sql.append(" WHERE Transactions.type IN (");
for (int ti = 0; ti < transactionTypes.length; ++ti) {
if (ti != 0)
sql.append(", ");
sql.append(transactionTypes[ti].value);
}
sql.append(")");
// Confirmation status
switch (confirmationStatus) {
case BOTH:
break;
case CONFIRMED:
sql.append(" AND Transactions.block_height IS NOT NULL");
break;
case UNCONFIRMED:
sql.append(" AND Transactions.block_height IS NULL");
break;
}
sql.append(" AND (RegisterNameTransactions.name = ?");
bindParams.add(name);
sql.append(" OR RegisterNameTransactions.reduced_name = ?");
bindParams.add(reducedName);
sql.append(" OR UpdateNameTransactions.name = ?");
bindParams.add(name);
sql.append(" OR (UpdateNameTransactions.reduced_new_name != '' AND UpdateNameTransactions.reduced_new_name = ?)");
bindParams.add(reducedName);
sql.append(" OR UpdateNameTransactions.new_name = ?");
bindParams.add(name);
sql.append(" OR SellNameTransactions.name = ?");
bindParams.add(name);
sql.append(" OR BuyNameTransactions.name = ?");
bindParams.add(name);
sql.append(") GROUP BY Transactions.signature, Transactions.created_when ORDER BY Transactions.created_when");
List<TransactionData> transactions = new ArrayList<>();
try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), bindParams.toArray())) {
if (resultSet == null)
return transactions;
do {
byte[] signature = resultSet.getBytes(1);
TransactionData transactionData = this.fromSignature(signature);
if (transactionData == null)
// Something inconsistent with the repository
throw new DataException("Unable to fetch name-related transaction from repository?");
transactions.add(transactionData);
} while (resultSet.next());
return transactions;
} catch (SQLException | DataException e) {
throw new DataException("Unable to fetch name-related transactions from repository", e);
}
}
@Override @Override
public List<TransactionData> getAssetTransactions(long assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse) public List<TransactionData> getAssetTransactions(long assetId, ConfirmationStatus confirmationStatus, Integer limit, Integer offset, Boolean reverse)
throws DataException { throws DataException {

View File

@ -125,7 +125,6 @@ public class IntegrityTests extends Common {
TransactionUtils.signAndMint(repository, updateTransactionData, alice); TransactionUtils.signAndMint(repository, updateTransactionData, alice);
// Register emoji name // Register emoji name
PrivateKeyAccount bob = Common.getTestAccount(repository, "bob");
String emojiName = "\uD83E\uDD73"; // Translates to a reducedName of "" String emojiName = "\uD83E\uDD73"; // Translates to a reducedName of ""
// Ensure that the initial_name isn't associated with the emoji name // Ensure that the initial_name isn't associated with the emoji name