From c49d06482361c8e98e0ad5b6472319e066968fd9 Mon Sep 17 00:00:00 2001 From: Toshihiro Suzuki Date: Fri, 16 Feb 2024 21:15:29 +0900 Subject: [PATCH] Add error codes to error messages (#1493) Co-authored-by: Josh Wong --- .../java/com/scalar/db/api/AuthAdmin.java | 34 +- .../com/scalar/db/api/ConditionBuilder.java | 9 +- .../java/com/scalar/db/api/GetBuilder.java | 9 +- .../com/scalar/db/api/LikeExpression.java | 20 +- core/src/main/java/com/scalar/db/api/Put.java | 3 +- .../java/com/scalar/db/api/ScanBuilder.java | 22 +- .../java/com/scalar/db/api/TableMetadata.java | 18 +- .../com/scalar/db/api/TransactionState.java | 5 +- ...AbstractDistributedTransactionManager.java | 31 +- .../com/scalar/db/common/AbstractResult.java | 3 +- ...tractTwoPhaseCommitTransactionManager.java | 43 +- ...nManagedDistributedTransactionManager.java | 8 +- ...nagedTwoPhaseCommitTransactionManager.java | 8 +- .../CheckedDistributedStorageAdmin.java | 131 +-- .../db/common/TableMetadataManager.java | 10 +- .../db/common/checker/OperationChecker.java | 78 +- .../com/scalar/db/common/error/Category.java | 24 + .../com/scalar/db/common/error/CoreError.java | 787 ++++++++++++++++++ .../scalar/db/common/error/ScalarDbError.java | 65 ++ .../com/scalar/db/config/ConfigUtils.java | 16 +- .../com/scalar/db/config/DatabaseConfig.java | 8 +- .../transaction/TransactionException.java | 2 +- .../java/com/scalar/db/io/BigIntColumn.java | 4 +- core/src/main/java/com/scalar/db/io/Key.java | 4 +- .../scalar/db/service/ProviderManager.java | 6 +- .../db/storage/cassandra/BatchHandler.java | 11 +- .../db/storage/cassandra/Cassandra.java | 9 +- .../db/storage/cassandra/CassandraAdmin.java | 9 +- .../cassandra/MutateStatementHandler.java | 21 +- .../cassandra/SelectStatementHandler.java | 14 + .../storage/cassandra/StatementHandler.java | 9 +- .../cassandra/StatementHandlerManager.java | 4 +- .../db/storage/cosmos/BatchHandler.java | 15 +- .../com/scalar/db/storage/cosmos/Cosmos.java | 9 +- .../scalar/db/storage/cosmos/CosmosAdmin.java | 15 +- .../db/storage/cosmos/CosmosConfig.java | 3 +- .../cosmos/CosmosOperationChecker.java | 4 +- .../scalar/db/storage/cosmos/CosmosUtils.java | 3 +- .../cosmos/MutateStatementHandler.java | 16 +- .../cosmos/SelectStatementHandler.java | 9 +- .../db/storage/dynamo/BatchHandler.java | 20 +- .../dynamo/DeleteStatementHandler.java | 11 +- .../com/scalar/db/storage/dynamo/Dynamo.java | 9 +- .../db/storage/dynamo/DynamoConfig.java | 3 +- .../dynamo/DynamoOperationChecker.java | 7 +- .../storage/dynamo/PutStatementHandler.java | 11 +- .../dynamo/SelectStatementHandler.java | 5 +- .../dynamo/bytes/BlobBytesEncoder.java | 4 +- .../dynamo/bytes/TextBytesEncoder.java | 4 +- .../com/scalar/db/storage/jdbc/JdbcAdmin.java | 21 +- .../scalar/db/storage/jdbc/JdbcConfig.java | 3 +- .../scalar/db/storage/jdbc/JdbcDatabase.java | 39 +- .../scalar/db/storage/jdbc/JdbcService.java | 6 +- .../db/storage/jdbc/RdbEngineFactory.java | 4 +- .../db/storage/jdbc/RdbEngineMysql.java | 22 +- .../db/storage/jdbc/RdbEngineOracle.java | 17 +- .../db/storage/jdbc/RdbEnginePostgresql.java | 27 +- .../db/storage/jdbc/RdbEngineSqlServer.java | 22 +- .../db/storage/jdbc/RdbEngineSqlite.java | 4 +- .../db/storage/jdbc/RdbEngineStrategy.java | 2 +- .../scalar/db/storage/jdbc/ScannerImpl.java | 5 +- .../storage/jdbc/query/SimpleSelectQuery.java | 2 +- .../db/storage/multistorage/MultiStorage.java | 3 +- .../multistorage/MultiStorageConfig.java | 7 +- .../consensuscommit/CommitHandler.java | 42 +- .../consensuscommit/ConsensusCommit.java | 11 +- .../consensuscommit/ConsensusCommitAdmin.java | 19 +- ...nsensusCommitMutationOperationChecker.java | 20 +- .../consensuscommit/ConsensusCommitUtils.java | 12 +- .../consensuscommit/CrudHandler.java | 29 +- .../MutationConditionsValidator.java | 16 +- .../transaction/consensuscommit/Snapshot.java | 41 +- .../TransactionTableMetadataManager.java | 10 +- .../TwoPhaseConsensusCommit.java | 27 +- .../db/transaction/jdbc/JdbcTransaction.java | 79 +- .../jdbc/JdbcTransactionAdmin.java | 2 + .../jdbc/JdbcTransactionManager.java | 10 +- .../com/scalar/db/util/ScalarDbUtils.java | 4 +- .../scalar/db/common/error/CoreErrorTest.java | 48 ++ .../StatementHandlerManagerTest.java | 2 +- .../scalar/db/storage/jdbc/JdbcAdminTest.java | 2 +- .../db/storage/jdbc/RdbEngineSqliteTest.java | 8 +- ...susCommitMutationOperationCheckerTest.java | 3 + .../TwoPhaseConsensusCommitManagerTest.java | 3 +- .../TwoPhaseConsensusCommitTest.java | 3 +- .../transaction/jdbc/JdbcTransactionTest.java | 14 +- 86 files changed, 1654 insertions(+), 508 deletions(-) create mode 100644 core/src/main/java/com/scalar/db/common/error/Category.java create mode 100644 core/src/main/java/com/scalar/db/common/error/CoreError.java create mode 100644 core/src/main/java/com/scalar/db/common/error/ScalarDbError.java create mode 100644 core/src/test/java/com/scalar/db/common/error/CoreErrorTest.java diff --git a/core/src/main/java/com/scalar/db/api/AuthAdmin.java b/core/src/main/java/com/scalar/db/api/AuthAdmin.java index 65ddc950fb..682b432db6 100644 --- a/core/src/main/java/com/scalar/db/api/AuthAdmin.java +++ b/core/src/main/java/com/scalar/db/api/AuthAdmin.java @@ -1,5 +1,6 @@ package com.scalar.db.api; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import java.util.List; import java.util.Optional; @@ -24,7 +25,8 @@ public interface AuthAdmin { */ default void createUser(String username, @Nullable String password, UserOption... userOptions) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -40,7 +42,8 @@ default void createUser(String username, @Nullable String password, UserOption.. */ default void alterUser(String username, @Nullable String password, UserOption... userOptions) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -51,7 +54,8 @@ default void alterUser(String username, @Nullable String password, UserOption... * @throws ExecutionException if the operation fails */ default void dropUser(String username) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -67,7 +71,8 @@ default void dropUser(String username) throws ExecutionException { default void grant( String username, String namespaceName, String tableName, Privilege... privileges) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -81,7 +86,8 @@ default void grant( */ default void grant(String username, String namespaceName, Privilege... privileges) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -97,7 +103,8 @@ default void grant(String username, String namespaceName, Privilege... privilege default void revoke( String username, String namespaceName, String tableName, Privilege... privileges) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -111,7 +118,8 @@ default void revoke( */ default void revoke(String username, String namespaceName, Privilege... privileges) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -122,7 +130,8 @@ default void revoke(String username, String namespaceName, Privilege... privileg * @throws ExecutionException if the operation fails */ default Optional getUser(String username) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -132,7 +141,8 @@ default Optional getUser(String username) throws ExecutionException { * @throws ExecutionException if the operation fails */ default List getUsers() throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -147,7 +157,8 @@ default List getUsers() throws ExecutionException { */ default Set getPrivileges(String username, String namespaceName, String tableName) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } /** @@ -161,7 +172,8 @@ default Set getPrivileges(String username, String namespaceName, Stri */ default Set getPrivileges(String username, String namespaceName) throws ExecutionException { - throw new UnsupportedOperationException("Not supported in the community edition"); + throw new UnsupportedOperationException( + CoreError.NOT_SUPPORTED_IN_COMMUNITY_EDITION.buildMessage()); } interface User { diff --git a/core/src/main/java/com/scalar/db/api/ConditionBuilder.java b/core/src/main/java/com/scalar/db/api/ConditionBuilder.java index 8f9fa99fa3..6c1b807781 100644 --- a/core/src/main/java/com/scalar/db/api/ConditionBuilder.java +++ b/core/src/main/java/com/scalar/db/api/ConditionBuilder.java @@ -1,6 +1,7 @@ package com.scalar.db.api; import com.scalar.db.api.ConditionalExpression.Operator; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.BigIntColumn; import com.scalar.db.io.BlobColumn; import com.scalar.db.io.BooleanColumn; @@ -802,8 +803,8 @@ private void check(ConditionalExpression conditionalExpression) { if (conditionalExpression.getOperator().equals(Operator.LIKE) || conditionalExpression.getOperator().equals(Operator.NOT_LIKE)) { throw new IllegalArgumentException( - "This condition is not allowed for the PutIf operation. Condition: " - + conditionalExpression); + CoreError.CONDITION_BUILD_ERROR_CONDITION_NOT_ALLOWED_FOR_PUT_IF.buildMessage( + conditionalExpression)); } } } @@ -843,8 +844,8 @@ private void check(ConditionalExpression conditionalExpression) { if (conditionalExpression.getOperator().equals(Operator.LIKE) || conditionalExpression.getOperator().equals(Operator.NOT_LIKE)) { throw new IllegalArgumentException( - "This condition is not allowed for the DeleteIf operation. Condition: " - + conditionalExpression); + CoreError.CONDITION_BUILD_ERROR_CONDITION_NOT_ALLOWED_FOR_DELETE_IF.buildMessage( + conditionalExpression)); } } } diff --git a/core/src/main/java/com/scalar/db/api/GetBuilder.java b/core/src/main/java/com/scalar/db/api/GetBuilder.java index a24a73456e..fadfcf214d 100644 --- a/core/src/main/java/com/scalar/db/api/GetBuilder.java +++ b/core/src/main/java/com/scalar/db/api/GetBuilder.java @@ -13,6 +13,7 @@ import com.scalar.db.api.OperationBuilder.PartitionKeyBuilder; import com.scalar.db.api.OperationBuilder.Projection; import com.scalar.db.api.OperationBuilder.TableBuilder; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.Key; import java.util.ArrayList; import java.util.Arrays; @@ -289,14 +290,18 @@ public BuildableGetOrGetWithIndexFromExisting clearNamespace() { private void checkNotGet() { if (!isGetWithIndex) { throw new UnsupportedOperationException( - "This operation is not supported when getting records of a database without using a secondary index"); + CoreError + .GET_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_GETTING_RECORDS_OF_DATABASE_WITHOUT_USING_INDEX + .buildMessage()); } } private void checkNotGetWithIndex() { if (isGetWithIndex) { throw new UnsupportedOperationException( - "This operation is not supported when getting records of a database using a secondary index"); + CoreError + .GET_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_GETTING_RECORDS_OF_DATABASE_USING_INDEX + .buildMessage()); } } diff --git a/core/src/main/java/com/scalar/db/api/LikeExpression.java b/core/src/main/java/com/scalar/db/api/LikeExpression.java index 22f058996e..f3df7d2c5e 100644 --- a/core/src/main/java/com/scalar/db/api/LikeExpression.java +++ b/core/src/main/java/com/scalar/db/api/LikeExpression.java @@ -1,6 +1,7 @@ package com.scalar.db.api; import com.google.common.base.MoreObjects; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.TextColumn; import java.util.Objects; import javax.annotation.Nonnull; @@ -40,29 +41,36 @@ public class LikeExpression extends ConditionalExpression { private void check(String pattern, Operator operator, String escape) { if (operator != Operator.LIKE && operator != Operator.NOT_LIKE) { throw new IllegalArgumentException( - "Operator must be like or not-like. Operator: " + operator); + CoreError.LIKE_CHECK_ERROR_OPERATOR_MUST_BE_LIKE_OR_NOT_LIKE.buildMessage(operator)); } if (escape == null || escape.length() > 1) { throw new IllegalArgumentException( - "Escape character must be a string of a single character or an empty string"); + CoreError + .LIKE_CHECK_ERROR_ESCAPE_CHARACTER_MUST_BE_STRING_OF_SINGLE_CHARACTER_OR_EMPTY_STRING + .buildMessage()); } if (pattern == null) { - throw new IllegalArgumentException("LIKE pattern must not be null"); + throw new IllegalArgumentException( + CoreError.LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_BE_NULL.buildMessage()); } - Character escapeChar = escape.length() == 0 ? null : escape.charAt(0); + Character escapeChar = escape.isEmpty() ? null : escape.charAt(0); char[] chars = pattern.toCharArray(); for (int i = 0; i < chars.length; i++) { char c = chars[i]; if (escapeChar != null && c == escapeChar && i + 1 < chars.length) { char nextChar = chars[++i]; if (nextChar != '_' && nextChar != '%' && nextChar != escapeChar) { - throw new IllegalArgumentException("LIKE pattern must not include only escape character"); + throw new IllegalArgumentException( + CoreError.LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_INCLUDE_ONLY_ESCAPE_CHARACTER + .buildMessage()); } } else if (escapeChar != null && c == escapeChar) { - throw new IllegalArgumentException("LIKE pattern must not end with escape character"); + throw new IllegalArgumentException( + CoreError.LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_END_WITH_ESCAPE_CHARACTER + .buildMessage()); } } } diff --git a/core/src/main/java/com/scalar/db/api/Put.java b/core/src/main/java/com/scalar/db/api/Put.java index 2dd5e9ad53..6709f030dc 100644 --- a/core/src/main/java/com/scalar/db/api/Put.java +++ b/core/src/main/java/com/scalar/db/api/Put.java @@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableSet; import com.scalar.db.api.PutBuilder.BuildableFromExisting; import com.scalar.db.api.PutBuilder.Namespace; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.BigIntColumn; import com.scalar.db.io.BlobColumn; import com.scalar.db.io.BooleanColumn; @@ -705,7 +706,7 @@ public Set getContainedColumnNames() { private void checkIfExists(String name) { if (!containsColumn(name)) { - throw new IllegalArgumentException(name + " doesn't exist"); + throw new IllegalArgumentException(CoreError.COLUMN_NOT_FOUND.buildMessage(name)); } } diff --git a/core/src/main/java/com/scalar/db/api/ScanBuilder.java b/core/src/main/java/com/scalar/db/api/ScanBuilder.java index 9e40950f28..d3b488de5e 100644 --- a/core/src/main/java/com/scalar/db/api/ScanBuilder.java +++ b/core/src/main/java/com/scalar/db/api/ScanBuilder.java @@ -26,6 +26,7 @@ import com.scalar.db.api.OperationBuilder.WhereAnd; import com.scalar.db.api.OperationBuilder.WhereOr; import com.scalar.db.api.Scan.Conjunction; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.Key; import java.util.ArrayList; import java.util.Arrays; @@ -858,37 +859,44 @@ public BuildableScanOrScanAllFromExisting clearNamespace() { private void checkNotScanWithIndexOrScanAll() { if (isScanWithIndex || isScanAll) { throw new UnsupportedOperationException( - "This operation is not supported when scanning all the records of a database " - + "or scanning records of a database using a secondary index"); + CoreError + .SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_ALL_RECORDS_OF_DATABASE_OR_SCANNING_RECORDS_OF_DATABASE_USING_INDEX + .buildMessage()); } } private void checkScanWithIndex() { if (!isScanWithIndex) { throw new UnsupportedOperationException( - "This operation is supported only when scanning records of a database using a secondary index"); + CoreError + .SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX + .buildMessage()); } } private void checkNotScanWithIndex() { if (isScanWithIndex) { throw new UnsupportedOperationException( - "This operation is not supported when scanning records of a database using a secondary index"); + CoreError + .SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX + .buildMessage()); } } private void checkScanAll() { if (!isScanAll) { throw new UnsupportedOperationException( - "This operation is supported only when scanning all the records of a database"); + CoreError + .SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_SCANNING_ALL_RECORDS_OF_DATABASE + .buildMessage()); } } private void checkConditionsEmpty() { if (!conjunctions.isEmpty()) { throw new IllegalStateException( - "This operation is supported only when no conditions are specified at all. " - + "If you want to modify the condition, please use clearConditions() to remove all existing conditions first"); + CoreError.SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_NO_CONDITIONS_ARE_SPECIFIED + .buildMessage()); } } diff --git a/core/src/main/java/com/scalar/db/api/TableMetadata.java b/core/src/main/java/com/scalar/db/api/TableMetadata.java index 5a200a78a3..fc2ae8d377 100644 --- a/core/src/main/java/com/scalar/db/api/TableMetadata.java +++ b/core/src/main/java/com/scalar/db/api/TableMetadata.java @@ -3,6 +3,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.scalar.db.api.Scan.Ordering.Order; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.DataType; import com.scalar.db.util.ImmutableLinkedHashSet; import java.util.HashMap; @@ -230,27 +231,28 @@ public Builder removeSecondaryIndex(String name) { public TableMetadata build() { if (columns.isEmpty()) { - throw new IllegalStateException("Need to specify one or more columns"); + throw new IllegalStateException( + CoreError.TABLE_METADATA_BUILD_ERROR_NO_COLUMNS_SPECIFIED.buildMessage()); } if (partitionKeyNames.isEmpty()) { - throw new IllegalStateException("Need to specify one or more partition keys"); + throw new IllegalStateException( + CoreError.TABLE_METADATA_BUILD_ERROR_NO_PARTITION_KEYS_SPECIFIED.buildMessage()); } partitionKeyNames.forEach( k -> { if (!columns.containsKey(k)) { throw new IllegalStateException( - "Need to specify the column definition of " - + k - + " specified as a partition key"); + CoreError.TABLE_METADATA_BUILD_ERROR_PARTITION_KEY_COLUMN_DEFINITION_NOT_SPECIFIED + .buildMessage(k)); } }); clusteringKeyNames.forEach( k -> { if (!columns.containsKey(k)) { throw new IllegalStateException( - "Need to specify the column definition of " - + k - + " specified as a clustering key"); + CoreError + .TABLE_METADATA_BUILD_ERROR_CLUSTERING_KEY_COLUMN_DEFINITION_NOT_SPECIFIED + .buildMessage(k)); } }); return new TableMetadata( diff --git a/core/src/main/java/com/scalar/db/api/TransactionState.java b/core/src/main/java/com/scalar/db/api/TransactionState.java index 6825bf6108..d2079e9c4a 100644 --- a/core/src/main/java/com/scalar/db/api/TransactionState.java +++ b/core/src/main/java/com/scalar/db/api/TransactionState.java @@ -1,5 +1,7 @@ package com.scalar.db.api; +import com.scalar.db.common.error.CoreError; + public enum TransactionState { PREPARED(1), DELETED(2), @@ -23,6 +25,7 @@ public static TransactionState getInstance(int id) { return state; } } - throw new IllegalArgumentException("Invalid id specified"); + throw new IllegalArgumentException( + CoreError.TRANSACTION_STATE_INSTANTIATION_ERROR_INVALID_ID.buildMessage(id)); } } diff --git a/core/src/main/java/com/scalar/db/common/AbstractDistributedTransactionManager.java b/core/src/main/java/com/scalar/db/common/AbstractDistributedTransactionManager.java index f81e16e657..05c4ae24bb 100644 --- a/core/src/main/java/com/scalar/db/common/AbstractDistributedTransactionManager.java +++ b/core/src/main/java/com/scalar/db/common/AbstractDistributedTransactionManager.java @@ -9,6 +9,7 @@ import com.scalar.db.api.Put; import com.scalar.db.api.Result; import com.scalar.db.api.Scan; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.transaction.AbortException; import com.scalar.db.exception.transaction.CommitException; @@ -16,11 +17,9 @@ import com.scalar.db.exception.transaction.RollbackException; import com.scalar.db.exception.transaction.TransactionException; import com.scalar.db.exception.transaction.UnknownTransactionStatusException; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.Nullable; public abstract class AbstractDistributedTransactionManager implements DistributedTransactionManager, DistributedTransactionDecoratorAddable { @@ -113,49 +112,49 @@ private enum Status { @Override public Optional get(Get get) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); return super.get(get); } @Override public List scan(Scan scan) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); return super.scan(scan); } @Override public void put(Put put) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.put(put); } @Override public void put(List puts) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.put(puts); } @Override public void delete(Delete delete) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.delete(delete); } @Override public void delete(List deletes) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.delete(deletes); } @Override public void mutate(List mutations) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.mutate(mutations); } @Override public void commit() throws CommitException, UnknownTransactionStatusException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); try { super.commit(); status = Status.COMMITTED; @@ -169,7 +168,7 @@ public void commit() throws CommitException, UnknownTransactionStatusException { public void rollback() throws RollbackException { if (status == Status.COMMITTED || status == Status.ROLLED_BACK) { throw new IllegalStateException( - "The transaction has already been committed or rolled back"); + CoreError.TRANSACTION_ALREADY_COMMITTED_OR_ROLLED_BACK.buildMessage(status)); } try { super.rollback(); @@ -181,7 +180,8 @@ public void rollback() throws RollbackException { @Override public void abort() throws AbortException { if (status == Status.COMMITTED || status == Status.ROLLED_BACK) { - throw new IllegalStateException("The transaction has already been committed or aborted"); + throw new IllegalStateException( + CoreError.TRANSACTION_ALREADY_COMMITTED_OR_ROLLED_BACK.buildMessage(status)); } try { super.abort(); @@ -190,10 +190,9 @@ public void abort() throws AbortException { } } - private void checkStatus(@Nullable String message, Status... expectedStatus) { - boolean expected = Arrays.stream(expectedStatus).anyMatch(s -> status == s); - if (!expected) { - throw new IllegalStateException(message); + private void checkIfActive() { + if (status != Status.ACTIVE) { + throw new IllegalStateException(CoreError.TRANSACTION_NOT_ACTIVE.buildMessage(status)); } } } diff --git a/core/src/main/java/com/scalar/db/common/AbstractResult.java b/core/src/main/java/com/scalar/db/common/AbstractResult.java index 743ad6b3b6..ec9b79b542 100644 --- a/core/src/main/java/com/scalar/db/common/AbstractResult.java +++ b/core/src/main/java/com/scalar/db/common/AbstractResult.java @@ -5,6 +5,7 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.scalar.db.api.Result; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.Value; import com.scalar.db.util.ScalarDbUtils; import java.util.ArrayList; @@ -47,7 +48,7 @@ public AbstractResult() { protected void checkIfExists(String name) { if (!contains(name)) { - throw new IllegalArgumentException(name + " doesn't exist"); + throw new IllegalArgumentException(CoreError.COLUMN_NOT_FOUND.buildMessage(name)); } } diff --git a/core/src/main/java/com/scalar/db/common/AbstractTwoPhaseCommitTransactionManager.java b/core/src/main/java/com/scalar/db/common/AbstractTwoPhaseCommitTransactionManager.java index ea8d9570f5..b1d563d4f4 100644 --- a/core/src/main/java/com/scalar/db/common/AbstractTwoPhaseCommitTransactionManager.java +++ b/core/src/main/java/com/scalar/db/common/AbstractTwoPhaseCommitTransactionManager.java @@ -9,6 +9,7 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.TwoPhaseCommitTransaction; import com.scalar.db.api.TwoPhaseCommitTransactionManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.transaction.AbortException; import com.scalar.db.exception.transaction.CommitException; @@ -18,11 +19,9 @@ import com.scalar.db.exception.transaction.TransactionException; import com.scalar.db.exception.transaction.UnknownTransactionStatusException; import com.scalar.db.exception.transaction.ValidationException; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; -import javax.annotation.Nullable; public abstract class AbstractTwoPhaseCommitTransactionManager implements TwoPhaseCommitTransactionManager, TwoPhaseCommitTransactionDecoratorAddable { @@ -119,49 +118,49 @@ private enum Status { @Override public Optional get(Get get) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); return super.get(get); } @Override public List scan(Scan scan) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); return super.scan(scan); } @Override public void put(Put put) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.put(put); } @Override public void put(List puts) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.put(puts); } @Override public void delete(Delete delete) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.delete(delete); } @Override public void delete(List deletes) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.delete(deletes); } @Override public void mutate(List mutations) throws CrudException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); super.mutate(mutations); } @Override public void prepare() throws PreparationException { - checkStatus("The transaction is not active", Status.ACTIVE); + checkIfActive(); try { super.prepare(); status = Status.PREPARED; @@ -173,7 +172,10 @@ public void prepare() throws PreparationException { @Override public void validate() throws ValidationException { - checkStatus("The transaction is not prepared", Status.PREPARED); + if (status != Status.PREPARED) { + throw new IllegalStateException(CoreError.TRANSACTION_NOT_PREPARED.buildMessage(status)); + } + try { super.validate(); status = Status.VALIDATED; @@ -185,8 +187,11 @@ public void validate() throws ValidationException { @Override public void commit() throws CommitException, UnknownTransactionStatusException { - checkStatus( - "The transaction is not prepared or validated.", Status.PREPARED, Status.VALIDATED); + if (status != Status.PREPARED && status != Status.VALIDATED) { + throw new IllegalStateException( + CoreError.TRANSACTION_NOT_PREPARED_OR_VALIDATED.buildMessage(status)); + } + try { super.commit(); status = Status.COMMITTED; @@ -200,7 +205,7 @@ public void commit() throws CommitException, UnknownTransactionStatusException { public void rollback() throws RollbackException { if (status == Status.COMMITTED || status == Status.ROLLED_BACK) { throw new IllegalStateException( - "The transaction has already been committed or rolled back"); + CoreError.TRANSACTION_ALREADY_COMMITTED_OR_ROLLED_BACK.buildMessage(status)); } try { super.rollback(); @@ -212,7 +217,8 @@ public void rollback() throws RollbackException { @Override public void abort() throws AbortException { if (status == Status.COMMITTED || status == Status.ROLLED_BACK) { - throw new IllegalStateException("The transaction has already been committed or aborted"); + throw new IllegalStateException( + CoreError.TRANSACTION_ALREADY_COMMITTED_OR_ROLLED_BACK.buildMessage(status)); } try { super.abort(); @@ -221,10 +227,9 @@ public void abort() throws AbortException { } } - private void checkStatus(@Nullable String message, Status... expectedStatus) { - boolean expected = Arrays.stream(expectedStatus).anyMatch(s -> status == s); - if (!expected) { - throw new IllegalStateException(message); + private void checkIfActive() { + if (status != Status.ACTIVE) { + throw new IllegalStateException(CoreError.TRANSACTION_NOT_ACTIVE.buildMessage(status)); } } } diff --git a/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedDistributedTransactionManager.java b/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedDistributedTransactionManager.java index eb8761c00b..7b52c643f6 100644 --- a/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedDistributedTransactionManager.java +++ b/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedDistributedTransactionManager.java @@ -7,6 +7,7 @@ import com.scalar.db.api.Put; import com.scalar.db.api.Result; import com.scalar.db.api.Scan; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.transaction.AbortException; import com.scalar.db.exception.transaction.CommitException; @@ -64,7 +65,8 @@ public void setTransactionExpirationHandler(BiConsumer new TransactionNotFoundException( - "A transaction associated with the specified transaction ID is not found. " - + "It might have been expired", - txId)); + CoreError.TRANSACTION_NOT_FOUND.buildMessage(), txId)); } @Override diff --git a/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedTwoPhaseCommitTransactionManager.java b/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedTwoPhaseCommitTransactionManager.java index 20597c2bcf..ba70346869 100644 --- a/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedTwoPhaseCommitTransactionManager.java +++ b/core/src/main/java/com/scalar/db/common/ActiveTransactionManagedTwoPhaseCommitTransactionManager.java @@ -7,6 +7,7 @@ import com.scalar.db.api.Result; import com.scalar.db.api.Scan; import com.scalar.db.api.TwoPhaseCommitTransaction; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.transaction.AbortException; import com.scalar.db.exception.transaction.CommitException; @@ -67,7 +68,8 @@ public void setTransactionExpirationHandler( private void add(ActiveTransaction transaction) throws TransactionException { if (activeTransactions.putIfAbsent(transaction.getId(), transaction).isPresent()) { transaction.rollback(); - throw new TransactionException("The transaction already exists", transaction.getId()); + throw new TransactionException( + CoreError.TRANSACTION_ALREADY_EXISTS.buildMessage(), transaction.getId()); } } @@ -86,9 +88,7 @@ public TwoPhaseCommitTransaction resume(String txId) throws TransactionNotFoundE .orElseThrow( () -> new TransactionNotFoundException( - "A transaction associated with the specified transaction ID is not found. " - + "It might have been expired", - txId)); + CoreError.TRANSACTION_NOT_FOUND.buildMessage(), txId)); } @Override diff --git a/core/src/main/java/com/scalar/db/common/CheckedDistributedStorageAdmin.java b/core/src/main/java/com/scalar/db/common/CheckedDistributedStorageAdmin.java index abb6e945bb..1626f8156c 100644 --- a/core/src/main/java/com/scalar/db/common/CheckedDistributedStorageAdmin.java +++ b/core/src/main/java/com/scalar/db/common/CheckedDistributedStorageAdmin.java @@ -2,6 +2,7 @@ import com.scalar.db.api.DistributedStorageAdmin; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -27,16 +28,18 @@ public CheckedDistributedStorageAdmin(DistributedStorageAdmin admin, DatabaseCon public void createNamespace(String namespace, Map options) throws ExecutionException { if (systemNamespaceName.equals(namespace)) { - throw new IllegalArgumentException(namespace + " is the system namespace name"); + throw new IllegalArgumentException( + CoreError.SYSTEM_NAMESPACE_SPECIFIED.buildMessage(namespace)); } if (namespaceExists(namespace)) { - throw new IllegalArgumentException("Namespace already exists: " + namespace); + throw new IllegalArgumentException( + CoreError.NAMESPACE_ALREADY_EXISTS.buildMessage(namespace)); } try { admin.createNamespace(namespace, options); } catch (ExecutionException e) { - throw new ExecutionException("Creating the namespace failed: " + namespace, e); + throw new ExecutionException(CoreError.CREATING_NAMESPACE_FAILED.buildMessage(namespace), e); } } @@ -45,18 +48,21 @@ public void createTable( String namespace, String table, TableMetadata metadata, Map options) throws ExecutionException { if (!namespaceExists(namespace)) { - throw new IllegalArgumentException("Namespace does not exist: " + namespace); + throw new IllegalArgumentException(CoreError.NAMESPACE_NOT_FOUND.buildMessage(namespace)); } if (tableExists(namespace, table)) { throw new IllegalArgumentException( - "Table already exists: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_ALREADY_EXISTS.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table))); } try { admin.createTable(namespace, table, metadata, options); } catch (ExecutionException e) { throw new ExecutionException( - "Creating the table failed: " + ScalarDbUtils.getFullTableName(namespace, table), e); + CoreError.CREATING_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } @@ -64,34 +70,37 @@ public void createTable( public void dropTable(String namespace, String table) throws ExecutionException { if (!tableExists(namespace, table)) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } try { admin.dropTable(namespace, table); } catch (ExecutionException e) { throw new ExecutionException( - "Dropping the table failed: " + ScalarDbUtils.getFullTableName(namespace, table), e); + CoreError.DROPPING_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } @Override public void dropNamespace(String namespace) throws ExecutionException { if (systemNamespaceName.equals(namespace)) { - throw new IllegalArgumentException(namespace + " is the system namespace name"); + throw new IllegalArgumentException( + CoreError.SYSTEM_NAMESPACE_SPECIFIED.buildMessage(namespace)); } if (!namespaceExists(namespace)) { - throw new IllegalArgumentException("Namespace does not exist: " + namespace); + throw new IllegalArgumentException(CoreError.NAMESPACE_NOT_FOUND.buildMessage(namespace)); } if (!getNamespaceTableNames(namespace).isEmpty()) { throw new IllegalArgumentException( - "Namespace is not empty: " + namespace + ", " + getNamespaceTableNames(namespace)); + CoreError.NAMESPACE_NOT_EMPTY.buildMessage(namespace, getNamespaceTableNames(namespace))); } try { admin.dropNamespace(namespace); } catch (ExecutionException e) { - throw new ExecutionException("Dropping the namespace failed: " + namespace, e); + throw new ExecutionException(CoreError.DROPPING_NAMESPACE_FAILED.buildMessage(namespace), e); } } @@ -99,14 +108,16 @@ public void dropNamespace(String namespace) throws ExecutionException { public void truncateTable(String namespace, String table) throws ExecutionException { if (!tableExists(namespace, table)) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } try { admin.truncateTable(namespace, table); } catch (ExecutionException e) { throw new ExecutionException( - "Truncating the table failed: " + ScalarDbUtils.getFullTableName(namespace, table), e); + CoreError.TRUNCATING_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } @@ -117,32 +128,26 @@ public void createIndex( TableMetadata tableMetadata = getTableMetadata(namespace, table); if (tableMetadata == null) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } if (!tableMetadata.getColumnNames().contains(columnName)) { throw new IllegalArgumentException( - "Column does not exist: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName); + CoreError.COLUMN_NOT_FOUND2.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName)); } if (indexExists(namespace, table, columnName)) { throw new IllegalArgumentException( - "Index already exists: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName); + CoreError.INDEX_ALREADY_EXISTS.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName)); } try { admin.createIndex(namespace, table, columnName, options); } catch (ExecutionException e) { throw new ExecutionException( - "Creating the index failed: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName, + CoreError.CREATING_INDEX_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName), e); } } @@ -152,25 +157,21 @@ public void dropIndex(String namespace, String table, String columnName) throws ExecutionException { if (!tableExists(namespace, table)) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } if (!indexExists(namespace, table, columnName)) { throw new IllegalArgumentException( - "Index does not exist: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName); + CoreError.INDEX_NOT_FOUND.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName)); } try { admin.dropIndex(namespace, table, columnName); } catch (ExecutionException e) { throw new ExecutionException( - "Dropping the index failed: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName, + CoreError.DROPPING_INDEX_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName), e); } } @@ -182,7 +183,8 @@ public TableMetadata getTableMetadata(String namespace, String table) throws Exe return admin.getTableMetadata(namespace, table); } catch (ExecutionException e) { throw new ExecutionException( - "Getting the table metadata failed: " + ScalarDbUtils.getFullTableName(namespace, table), + CoreError.GETTING_TABLE_METADATA_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), e); } } @@ -193,7 +195,7 @@ public Set getNamespaceTableNames(String namespace) throws ExecutionExce return admin.getNamespaceTableNames(namespace); } catch (ExecutionException e) { throw new ExecutionException( - "Getting the table names in the namespace failed: " + namespace, e); + CoreError.GETTING_TABLE_NAMES_IN_NAMESPACE_FAILED.buildMessage(namespace), e); } } @@ -205,7 +207,8 @@ public boolean namespaceExists(String namespace) throws ExecutionException { try { return admin.namespaceExists(namespace); } catch (ExecutionException e) { - throw new ExecutionException("Checking if the namespace exists failed: " + namespace, e); + throw new ExecutionException( + CoreError.CHECKING_NAMESPACE_EXISTENCE_FAILED.buildMessage(namespace), e); } } @@ -215,8 +218,8 @@ public boolean tableExists(String namespace, String table) throws ExecutionExcep return DistributedStorageAdmin.super.tableExists(namespace, table); } catch (ExecutionException e) { throw new ExecutionException( - "Checking if the table exists failed: " - + ScalarDbUtils.getFullTableName(namespace, table), + CoreError.CHECKING_TABLE_EXISTENCE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), e); } } @@ -228,10 +231,8 @@ public boolean indexExists(String namespace, String table, String columnName) return DistributedStorageAdmin.super.indexExists(namespace, table, columnName); } catch (ExecutionException e) { throw new ExecutionException( - "Checking if the index exists failed: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName, + CoreError.CHECKING_INDEX_EXISTENCE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName), e); } } @@ -242,7 +243,7 @@ public void repairNamespace(String namespace, Map options) try { admin.repairNamespace(namespace, options); } catch (ExecutionException e) { - throw new ExecutionException("Repairing the namespace failed: " + namespace, e); + throw new ExecutionException(CoreError.REPAIRING_NAMESPACE_FAILED.buildMessage(namespace), e); } } @@ -254,7 +255,9 @@ public void repairTable( admin.repairTable(namespace, table, metadata, options); } catch (ExecutionException e) { throw new ExecutionException( - "Repairing the table failed: " + ScalarDbUtils.getFullTableName(namespace, table), e); + CoreError.REPAIRING_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } @@ -265,25 +268,21 @@ public void addNewColumnToTable( TableMetadata tableMetadata = getTableMetadata(namespace, table); if (tableMetadata == null) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } if (tableMetadata.getColumnNames().contains(columnName)) { throw new IllegalArgumentException( - "Column already exists: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName); + CoreError.COLUMN_ALREADY_EXISTS.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName)); } try { admin.addNewColumnToTable(namespace, table, columnName, columnType); } catch (ExecutionException e) { throw new ExecutionException( - "Adding new column to the table failed: " - + ScalarDbUtils.getFullTableName(namespace, table) - + ", " - + columnName, + CoreError.ADDING_NEW_COLUMN_TO_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName, columnType), e); } } @@ -296,11 +295,12 @@ public Set getNamespaceNames() throws ExecutionException { return namespaceNames; } + // If the system namespace does not exist, add it to the set namespaceNames = new HashSet<>(namespaceNames); namespaceNames.add(systemNamespaceName); return namespaceNames; } catch (ExecutionException e) { - throw new ExecutionException("Getting the namespace names failed", e); + throw new ExecutionException(CoreError.GETTING_NAMESPACE_NAMES_FAILED.buildMessage(), e); } } @@ -311,8 +311,8 @@ public TableMetadata getImportTableMetadata(String namespace, String table) return admin.getImportTableMetadata(namespace, table); } catch (ExecutionException e) { throw new ExecutionException( - "Getting the table metadata of the importing table failed: " - + ScalarDbUtils.getFullTableName(namespace, table), + CoreError.GETTING_IMPORT_TABLE_METADATA_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), e); } } @@ -323,14 +323,17 @@ public void importTable(String namespace, String table, Map opti TableMetadata tableMetadata = getTableMetadata(namespace, table); if (tableMetadata != null) { throw new IllegalArgumentException( - "Table already exists: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_ALREADY_EXISTS.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table))); } try { admin.importTable(namespace, table, options); } catch (ExecutionException e) { throw new ExecutionException( - "Importing the table failed: " + ScalarDbUtils.getFullTableName(namespace, table), e); + CoreError.IMPORTING_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } @@ -342,8 +345,8 @@ public void addRawColumnToTable( admin.addRawColumnToTable(namespace, table, columnName, columnType); } catch (ExecutionException e) { throw new ExecutionException( - "Adding the raw column to the table failed: " - + ScalarDbUtils.getFullTableName(namespace, table), + CoreError.ADDING_RAW_COLUMN_TO_TABLE_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table), columnName, columnType), e); } } @@ -353,7 +356,7 @@ public void upgrade(Map options) throws ExecutionException { try { admin.upgrade(options); } catch (ExecutionException e) { - throw new ExecutionException("Upgrading the ScalarDB environment failed", e); + throw new ExecutionException(CoreError.UPGRADING_SCALAR_DB_ENV_FAILED.buildMessage(), e); } } diff --git a/core/src/main/java/com/scalar/db/common/TableMetadataManager.java b/core/src/main/java/com/scalar/db/common/TableMetadataManager.java index 5fbadf306d..247f33681b 100644 --- a/core/src/main/java/com/scalar/db/common/TableMetadataManager.java +++ b/core/src/main/java/com/scalar/db/common/TableMetadataManager.java @@ -6,7 +6,9 @@ import com.scalar.db.api.Admin; import com.scalar.db.api.Operation; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; +import com.scalar.db.util.ScalarDbUtils; import com.scalar.db.util.ThrowableFunction; import java.util.Objects; import java.util.Optional; @@ -53,7 +55,8 @@ public Optional load(@Nonnull TableKey key) throws Exception { */ public TableMetadata getTableMetadata(Operation operation) throws ExecutionException { if (!operation.forNamespace().isPresent() || !operation.forTable().isPresent()) { - throw new IllegalArgumentException("Operation has no target namespace and table name"); + throw new IllegalArgumentException( + CoreError.OPERATION_DOES_NOT_HAVE_TARGET_NAMESPACE_OR_TABLE_NAME.buildMessage(operation)); } return getTableMetadata(operation.forNamespace().get(), operation.forTable().get()); } @@ -71,7 +74,10 @@ public TableMetadata getTableMetadata(String namespace, String table) throws Exe TableKey key = new TableKey(namespace, table); return tableMetadataCache.get(key).orElse(null); } catch (java.util.concurrent.ExecutionException e) { - throw new ExecutionException("Getting a table metadata failed", e); + throw new ExecutionException( + CoreError.GETTING_TABLE_METADATA_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } diff --git a/core/src/main/java/com/scalar/db/common/checker/OperationChecker.java b/core/src/main/java/com/scalar/db/common/checker/OperationChecker.java index b264e5d190..6bd1b336b1 100644 --- a/core/src/main/java/com/scalar/db/common/checker/OperationChecker.java +++ b/core/src/main/java/com/scalar/db/common/checker/OperationChecker.java @@ -13,6 +13,7 @@ import com.scalar.db.api.Selection; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.Column; @@ -44,26 +45,28 @@ public void check(Get get) throws ExecutionException { if (ScalarDbUtils.isSecondaryIndexSpecified(get, metadata)) { if (get.getPartitionKey().size() != 1) { throw new IllegalArgumentException( - "Only a single column index is supported. Operation: " + get); + CoreError.OPERATION_CHECK_ERROR_INDEX_ONLY_SINGLE_COLUMN_INDEX_SUPPORTED.buildMessage( + get)); } String name = get.getPartitionKey().getColumns().get(0).getName(); if (!metadata.getSecondaryIndexNames().contains(name)) { throw new IllegalArgumentException( - "The column of the specified index key is not indexed. Operation: " + get); + CoreError.OPERATION_CHECK_ERROR_INDEX_NON_INDEXED_COLUMN_SPECIFIED.buildMessage(get)); } if (!new ColumnChecker(metadata, true, false, false, false) .check(get.getPartitionKey().getColumns().get(0))) { throw new IllegalArgumentException( - "The index key is not properly specified. Operation: " + get); + CoreError.OPERATION_CHECK_ERROR_INDEX_INDEX_KEY_NOT_PROPERLY_SPECIFIED.buildMessage( + get)); } // The following check is not needed when we use GetWithIndex. But we need to keep it for // backward compatibility. We will remove it in release 5.0.0. if (get.getClusteringKey().isPresent()) { throw new IllegalArgumentException( - "Clustering keys cannot be specified when using an index. Operation: " + get); + CoreError.OPERATION_CHECK_ERROR_INDEX_CLUSTERING_KEY_SPECIFIED.buildMessage(get)); } return; } @@ -84,31 +87,33 @@ public void check(Scan scan) throws ExecutionException { if (ScalarDbUtils.isSecondaryIndexSpecified(scan, metadata)) { if (scan.getPartitionKey().size() != 1) { throw new IllegalArgumentException( - "Only a single column index is supported. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_INDEX_ONLY_SINGLE_COLUMN_INDEX_SUPPORTED.buildMessage( + scan)); } String name = scan.getPartitionKey().getColumns().get(0).getName(); if (!metadata.getSecondaryIndexNames().contains(name)) { throw new IllegalArgumentException( - "The column of the specified index key is not indexed. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_INDEX_NON_INDEXED_COLUMN_SPECIFIED.buildMessage(scan)); } if (!new ColumnChecker(metadata, true, false, false, false) .check(scan.getPartitionKey().getColumns().get(0))) { throw new IllegalArgumentException( - "The index key is not properly specified. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_INDEX_INDEX_KEY_NOT_PROPERLY_SPECIFIED.buildMessage( + scan)); } // The following checks are not needed when we use ScanWithIndex. But we need to keep them for // backward compatibility. We will remove them in release 5.0.0. if (scan.getStartClusteringKey().isPresent() || scan.getEndClusteringKey().isPresent()) { throw new IllegalArgumentException( - "Clustering keys cannot be specified when using an index. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_INDEX_CLUSTERING_KEY_SPECIFIED.buildMessage(scan)); } if (!scan.getOrderings().isEmpty()) { throw new IllegalArgumentException( - "Orderings cannot be specified when using an index. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_INDEX_ORDERING_SPECIFIED.buildMessage(scan)); } return; } @@ -118,7 +123,7 @@ public void check(Scan scan) throws ExecutionException { checkClusteringKeys(scan, metadata); if (scan.getLimit() < 0) { - throw new IllegalArgumentException("The limit cannot be negative. Operation: " + scan); + throw new IllegalArgumentException(CoreError.OPERATION_CHECK_ERROR_LIMIT.buildMessage(scan)); } checkOrderings(scan, metadata); @@ -127,7 +132,7 @@ public void check(Scan scan) throws ExecutionException { private void check(ScanAll scanAll) throws ExecutionException { if (!config.isCrossPartitionScanEnabled()) { throw new IllegalArgumentException( - "Cross-partition scan is not enabled. Operation: " + scanAll); + CoreError.OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN.buildMessage(scanAll)); } TableMetadata metadata = getTableMetadata(scanAll); @@ -135,18 +140,19 @@ private void check(ScanAll scanAll) throws ExecutionException { checkProjections(scanAll, metadata); if (scanAll.getLimit() < 0) { - throw new IllegalArgumentException("The limit cannot be negative. Operation: " + scanAll); + throw new IllegalArgumentException( + CoreError.OPERATION_CHECK_ERROR_LIMIT.buildMessage(scanAll)); } if (!config.isCrossPartitionScanOrderingEnabled() && !scanAll.getOrderings().isEmpty()) { throw new IllegalArgumentException( - "Cross-partition scan ordering is not enabled. Operation: " + scanAll); + CoreError.OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN_ORDERING.buildMessage(scanAll)); } checkOrderings(scanAll, metadata); if (!config.isCrossPartitionScanFilteringEnabled() && !scanAll.getConjunctions().isEmpty()) { throw new IllegalArgumentException( - "Cross-partition scan filtering is not enabled. Operation: " + scanAll); + CoreError.OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN_FILTERING.buildMessage(scanAll)); } checkConjunctions(scanAll, metadata); } @@ -155,10 +161,7 @@ private void checkProjections(Selection selection, TableMetadata metadata) { for (String projection : selection.getProjections()) { if (!metadata.getColumnNames().contains(projection)) { throw new IllegalArgumentException( - "The specified projection is not found. Invalid projection: " - + projection - + ", Operation: " - + selection); + CoreError.OPERATION_CHECK_ERROR_PROJECTION.buildMessage(projection, selection)); } } } @@ -173,7 +176,7 @@ private void checkClusteringKeys(Scan scan, TableMetadata metadata) { Key startClusteringKey = scan.getStartClusteringKey().get(); Key endClusteringKey = scan.getEndClusteringKey().get(); Supplier message = - () -> "The clustering key boundary is not properly specified. Operation: " + scan; + () -> CoreError.OPERATION_CHECK_ERROR_CLUSTERING_KEY_BOUNDARY.buildMessage(scan); if (startClusteringKey.size() != endClusteringKey.size()) { throw new IllegalArgumentException(message.get()); @@ -195,7 +198,7 @@ private void checkStartClusteringKey(Scan scan, TableMetadata metadata) { ckey -> { if (!checkKey(ckey, metadata.getClusteringKeyNames(), true, metadata)) { throw new IllegalArgumentException( - "The start clustering key is not properly specified. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_START_CLUSTERING_KEY.buildMessage(scan)); } }); } @@ -206,7 +209,7 @@ private void checkEndClusteringKey(Scan scan, TableMetadata metadata) { ckey -> { if (!checkKey(ckey, metadata.getClusteringKeyNames(), true, metadata)) { throw new IllegalArgumentException( - "The end clustering key is not properly specified. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_END_CLUSTERING_KEY.buildMessage(scan)); } }); } @@ -217,7 +220,8 @@ private void checkOrderings(Scan scan, TableMetadata metadata) { return; } - Supplier message = () -> "Orderings are not properly specified. Operation: " + scan; + Supplier message = + () -> CoreError.OPERATION_CHECK_ERROR_ORDERING_NOT_PROPERLY_SPECIFIED.buildMessage(scan); if (orderings.size() > metadata.getClusteringKeyNames().size()) { throw new IllegalArgumentException(message.get()); @@ -243,14 +247,12 @@ private void checkOrderings(Scan scan, TableMetadata metadata) { } } - private void checkOrderings(ScanAll scan, TableMetadata metadata) { - for (Scan.Ordering ordering : scan.getOrderings()) { + private void checkOrderings(ScanAll scanAll, TableMetadata metadata) { + for (Scan.Ordering ordering : scanAll.getOrderings()) { if (!metadata.getColumnNames().contains(ordering.getColumnName())) { throw new IllegalArgumentException( - "The specified ordering column is not found. Invalid ordering: " - + ordering - + ", Operation: " - + scan); + CoreError.OPERATION_CHECK_ERROR_ORDERING_COLUMN_NOT_FOUND.buildMessage( + ordering, scanAll)); } } } @@ -271,7 +273,7 @@ private void checkConjunctions(ScanAll scan, TableMetadata metadata) { } if (!isValid) { throw new IllegalArgumentException( - "The condition is not properly specified. Operation: " + scan); + CoreError.OPERATION_CHECK_ERROR_CONDITION.buildMessage(scan)); } } } @@ -293,8 +295,9 @@ public void check(Delete delete) throws ExecutionException { protected TableMetadata getTableMetadata(Operation operation) throws ExecutionException { TableMetadata metadata = tableMetadataManager.getTableMetadata(operation); if (metadata == null) { + assert operation.forFullTableName().isPresent(); throw new IllegalArgumentException( - "The specified table is not found: " + operation.forFullTableName().get()); + CoreError.TABLE_NOT_FOUND.buildMessage(operation.forFullTableName().get())); } return metadata; } @@ -303,10 +306,7 @@ private void checkColumnsInPut(Put put, TableMetadata metadata) { for (Column column : put.getColumns().values()) { if (!new ColumnChecker(metadata, false, false, false, true).check(column)) { throw new IllegalArgumentException( - "The column value is not properly specified. Invalid column: " - + column - + ", Operation: " - + put); + CoreError.OPERATION_CHECK_ERROR_INVALID_COLUMN.buildMessage(column, put)); } } } @@ -319,14 +319,14 @@ private void checkCondition(Mutation mutation, TableMetadata metadata) { c -> { if (!new ConditionChecker(metadata).check(mutation.getCondition().get(), isPut)) { throw new IllegalArgumentException( - "The condition is not properly specified. Operation: " + mutation); + CoreError.OPERATION_CHECK_ERROR_CONDITION.buildMessage(mutation)); } }); } public void check(List mutations) throws ExecutionException { if (mutations.isEmpty()) { - throw new IllegalArgumentException("The mutations are empty"); + throw new IllegalArgumentException(CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); } Mutation first = mutations.get(0); @@ -335,7 +335,7 @@ public void check(List mutations) throws ExecutionException || !mutation.forTable().equals(first.forTable()) || !mutation.getPartitionKey().equals(first.getPartitionKey())) { throw new IllegalArgumentException( - "Mutations that span multi-partition are not supported. Mutations: " + mutations); + CoreError.OPERATION_CHECK_ERROR_MULTI_PARTITION_MUTATION.buildMessage(mutations)); } } @@ -357,13 +357,13 @@ private void checkPrimaryKey(Operation operation, TableMetadata metadata) { private void checkPartitionKey(Operation operation, TableMetadata metadata) { if (!checkKey(operation.getPartitionKey(), metadata.getPartitionKeyNames(), false, metadata)) { throw new IllegalArgumentException( - "The partition key is not properly specified. Operation: " + operation); + CoreError.OPERATION_CHECK_ERROR_PARTITION_KEY.buildMessage(operation)); } } private void checkClusteringKey(Operation operation, TableMetadata metadata) { Supplier message = - () -> "The clustering key is not properly specified. Operation: " + operation; + () -> CoreError.OPERATION_CHECK_ERROR_CLUSTERING_KEY.buildMessage(operation); if (!metadata.getClusteringKeyNames().isEmpty() && !operation.getClusteringKey().isPresent()) { throw new IllegalArgumentException(message.get()); diff --git a/core/src/main/java/com/scalar/db/common/error/Category.java b/core/src/main/java/com/scalar/db/common/error/Category.java new file mode 100644 index 0000000000..a4c6a4ffee --- /dev/null +++ b/core/src/main/java/com/scalar/db/common/error/Category.java @@ -0,0 +1,24 @@ +package com.scalar.db.common.error; + +import java.util.Objects; + +public enum Category { + USER_ERROR("1"), + CONCURRENCY_ERROR("2"), + INTERNAL_ERROR("3"), + UNKNOWN_TRANSACTION_STATUS_ERROR("4"); + + private final String id; + + Category(String id) { + Objects.requireNonNull(id, "The id must not be null."); + if (id.length() != 1) { + throw new IllegalArgumentException("The length of the id must be 1"); + } + this.id = id; + } + + public String getId() { + return id; + } +} diff --git a/core/src/main/java/com/scalar/db/common/error/CoreError.java b/core/src/main/java/com/scalar/db/common/error/CoreError.java new file mode 100644 index 0000000000..5d1b2bb12b --- /dev/null +++ b/core/src/main/java/com/scalar/db/common/error/CoreError.java @@ -0,0 +1,787 @@ +package com.scalar.db.common.error; + +public enum CoreError implements ScalarDbError { + + // + // Errors for the user error category + // + OPERATION_CHECK_ERROR_INDEX_ONLY_SINGLE_COLUMN_INDEX_SUPPORTED( + Category.USER_ERROR, + "0000", + "Only a single-column index is supported. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_INDEX_NON_INDEXED_COLUMN_SPECIFIED( + Category.USER_ERROR, + "0001", + "The column of the specified index key is not indexed. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_INDEX_INDEX_KEY_NOT_PROPERLY_SPECIFIED( + Category.USER_ERROR, + "0002", + "The index key is not properly specified. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_INDEX_CLUSTERING_KEY_SPECIFIED( + Category.USER_ERROR, + "0003", + "Clustering keys cannot be specified when using an index. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_INDEX_ORDERING_SPECIFIED( + Category.USER_ERROR, + "0004", + "Orderings cannot be specified when using an index. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_LIMIT( + Category.USER_ERROR, "0005", "The limit cannot be negative. Operation: %s", "", ""), + OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN( + Category.USER_ERROR, "0006", "Cross-partition scan is not enabled. Operation: %s", "", ""), + OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN_ORDERING( + Category.USER_ERROR, + "0007", + "Cross-partition scan ordering is not enabled. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_CROSS_PARTITION_SCAN_FILTERING( + Category.USER_ERROR, + "0008", + "Cross-partition scan filtering is not enabled. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_PROJECTION( + Category.USER_ERROR, + "0009", + "The specified projection is not found. Projection: %s, Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_CLUSTERING_KEY_BOUNDARY( + Category.USER_ERROR, + "0010", + "The clustering key boundary is not properly specified. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_START_CLUSTERING_KEY( + Category.USER_ERROR, + "0011", + "The start clustering key is not properly specified. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_END_CLUSTERING_KEY( + Category.USER_ERROR, + "0012", + "The end clustering key is not properly specified. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_ORDERING_NOT_PROPERLY_SPECIFIED( + Category.USER_ERROR, "0013", "Orderings are not properly specified. Operation: %s", "", ""), + OPERATION_CHECK_ERROR_ORDERING_COLUMN_NOT_FOUND( + Category.USER_ERROR, + "0014", + "The specified ordering column is not found. Ordering: %s, Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_CONDITION( + Category.USER_ERROR, + "0015", + "The condition is not properly specified. Operation: %s", + "", + ""), + TABLE_NOT_FOUND(Category.USER_ERROR, "0016", "Table does not exist. Table: %s", "", ""), + OPERATION_CHECK_ERROR_INVALID_COLUMN( + Category.USER_ERROR, + "0017", + "The column value is not properly specified. Column: %s, Operation: %s", + "", + ""), + EMPTY_MUTATIONS_SPECIFIED(Category.USER_ERROR, "0018", "The mutations are empty", "", ""), + OPERATION_CHECK_ERROR_MULTI_PARTITION_MUTATION( + Category.USER_ERROR, + "0019", + "Mutations that span multiple partitions are not supported. Mutations: %s", + "", + ""), + OPERATION_CHECK_ERROR_PARTITION_KEY( + Category.USER_ERROR, + "0020", + "The partition key is not properly specified. Operation: %s", + "", + ""), + OPERATION_CHECK_ERROR_CLUSTERING_KEY( + Category.USER_ERROR, + "0021", + "The clustering key is not properly specified. Operation: %s", + "", + ""), + NOT_SUPPORTED_IN_COMMUNITY_EDITION( + Category.USER_ERROR, + "0022", + "This feature is not supported in the ScalarDB Community edition", + "", + ""), + CONDITION_BUILD_ERROR_CONDITION_NOT_ALLOWED_FOR_PUT_IF( + Category.USER_ERROR, + "0023", + "This condition is not allowed for the PutIf operation. Condition: %s", + "", + ""), + CONDITION_BUILD_ERROR_CONDITION_NOT_ALLOWED_FOR_DELETE_IF( + Category.USER_ERROR, + "0024", + "This condition is not allowed for the DeleteIf operation. Condition: %s", + "", + ""), + LIKE_CHECK_ERROR_OPERATOR_MUST_BE_LIKE_OR_NOT_LIKE( + Category.USER_ERROR, "0025", "Operator must be LIKE or NOT_LIKE. Operator: %s", "", ""), + LIKE_CHECK_ERROR_ESCAPE_CHARACTER_MUST_BE_STRING_OF_SINGLE_CHARACTER_OR_EMPTY_STRING( + Category.USER_ERROR, + "0026", + "An escape character must be a string of a single character or an empty string", + "", + ""), + LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_BE_NULL( + Category.USER_ERROR, "0027", "The LIKE pattern must not be null", "", ""), + LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_INCLUDE_ONLY_ESCAPE_CHARACTER( + Category.USER_ERROR, + "0028", + "The LIKE pattern must not include only an escape character", + "", + ""), + LIKE_CHECK_ERROR_LIKE_PATTERN_MUST_NOT_END_WITH_ESCAPE_CHARACTER( + Category.USER_ERROR, + "0029", + "The LIKE pattern must not end with an escape character", + "", + ""), + COLUMN_NOT_FOUND(Category.USER_ERROR, "0030", "Column %s does not exist", "", ""), + GET_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_GETTING_RECORDS_OF_DATABASE_WITHOUT_USING_INDEX( + Category.USER_ERROR, + "0031", + "This operation is not supported when getting records of a database without using an index", + "", + ""), + GET_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_GETTING_RECORDS_OF_DATABASE_USING_INDEX( + Category.USER_ERROR, + "0032", + "This operation is not supported when getting records of a database by using an index", + "", + ""), + SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_ALL_RECORDS_OF_DATABASE_OR_SCANNING_RECORDS_OF_DATABASE_USING_INDEX( + Category.USER_ERROR, + "0033", + "This operation is not supported when scanning all the records of a database " + + "or scanning records of a database by using an index", + "", + ""), + SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX( + Category.USER_ERROR, + "0034", + "This operation is supported only when scanning records of a database by using an index", + "", + ""), + SCAN_BUILD_ERROR_OPERATION_NOT_SUPPORTED_WHEN_SCANNING_RECORDS_OF_DATABASE_USING_INDEX( + Category.USER_ERROR, + "0035", + "This operation is not supported when scanning records of a database by using an index", + "", + ""), + SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_SCANNING_ALL_RECORDS_OF_DATABASE( + Category.USER_ERROR, + "0036", + "This operation is supported only when scanning all the records of a database", + "", + ""), + SCAN_BUILD_ERROR_OPERATION_SUPPORTED_ONLY_WHEN_NO_CONDITIONS_ARE_SPECIFIED( + Category.USER_ERROR, + "0037", + "This operation is supported only when no conditions are specified at all. " + + "If you want to modify the condition, please use clearConditions() to remove all existing conditions first", + "", + ""), + TABLE_METADATA_BUILD_ERROR_NO_COLUMNS_SPECIFIED( + Category.USER_ERROR, "0038", "One or more columns must be specified.", "", ""), + TABLE_METADATA_BUILD_ERROR_NO_PARTITION_KEYS_SPECIFIED( + Category.USER_ERROR, "0039", "One or more partition keys must be specified.", "", ""), + TABLE_METADATA_BUILD_ERROR_PARTITION_KEY_COLUMN_DEFINITION_NOT_SPECIFIED( + Category.USER_ERROR, + "0040", + "The column definition must be specified since %s is specified as a partition key", + "", + ""), + TABLE_METADATA_BUILD_ERROR_CLUSTERING_KEY_COLUMN_DEFINITION_NOT_SPECIFIED( + Category.USER_ERROR, + "0041", + "The column definition must be specified since %s is specified as a clustering key", + "", + ""), + TRANSACTION_STATE_INSTANTIATION_ERROR_INVALID_ID( + Category.USER_ERROR, "0042", "Invalid ID specified. ID: %d", "", ""), + TRANSACTION_NOT_ACTIVE( + Category.USER_ERROR, "0043", "The transaction is not active. Status: %s", "", ""), + TRANSACTION_ALREADY_COMMITTED_OR_ROLLED_BACK( + Category.USER_ERROR, + "0044", + "The transaction has already been committed or rolled back. Status: %s", + "", + ""), + TRANSACTION_NOT_PREPARED( + Category.USER_ERROR, "0045", "The transaction has not been prepared. Status: %s", "", ""), + TRANSACTION_NOT_PREPARED_OR_VALIDATED( + Category.USER_ERROR, + "0046", + "The transaction has not been prepared or validated. Status: %s", + "", + ""), + TRANSACTION_ALREADY_EXISTS(Category.USER_ERROR, "0047", "The transaction already exists", "", ""), + TRANSACTION_NOT_FOUND( + Category.USER_ERROR, + "0048", + "A transaction associated with the specified transaction ID is not found. " + + "The transaction might have expired", + "", + ""), + SYSTEM_NAMESPACE_SPECIFIED( + Category.USER_ERROR, "0049", "%s is the system namespace name", "", ""), + NAMESPACE_ALREADY_EXISTS( + Category.USER_ERROR, "0050", "The namespace already exists. Namespace: %s", "", ""), + NAMESPACE_NOT_FOUND( + Category.USER_ERROR, "0051", "The namespace does not exist. Namespace: %s", "", ""), + TABLE_ALREADY_EXISTS(Category.USER_ERROR, "0052", "The table already exists. Table: %s", "", ""), + NAMESPACE_NOT_EMPTY( + Category.USER_ERROR, + "0053", + "The namespace is not empty. Namespace: %s; Tables in the namespace: %s", + "", + ""), + COLUMN_NOT_FOUND2( + Category.USER_ERROR, "0054", "The column does not exist. Table: %s; Column: %s", "", ""), + INDEX_ALREADY_EXISTS( + Category.USER_ERROR, "0055", "The index already exists. Table: %s; Column: %s", "", ""), + INDEX_NOT_FOUND( + Category.USER_ERROR, "0056", "The index does not exist. Table: %s; Column: %s", "", ""), + COLUMN_ALREADY_EXISTS( + Category.USER_ERROR, "0057", "The column already exists. Table: %s; Column: %s", "", ""), + OPERATION_DOES_NOT_HAVE_TARGET_NAMESPACE_OR_TABLE_NAME( + Category.USER_ERROR, + "0058", + "The operation does not have the target namespace or table name. Operation: %s", + "", + ""), + CONFIG_UTILS_INVALID_NUMBER_FORMAT( + Category.USER_ERROR, + "0059", + "The specified value of the property '%s' is not a number. Value: %s", + "", + ""), + CONFIG_UTILS_INVALID_BOOLEAN_FORMAT( + Category.USER_ERROR, + "0060", + "The specified value of the property '%s' is not a boolean. Value: %s", + "", + ""), + CONFIG_UTILS_READING_FILE_FAILED( + Category.USER_ERROR, "0061", "Reading the file failed. File: %s", "", ""), + CROSS_PARTITION_SCAN_MUST_BE_ENABLED_TO_USE_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING( + Category.USER_ERROR, + "0062", + "The property 'scalar.db.cross_partition_scan.enabled' must be set to true " + + "to use cross-partition scan with filtering or ordering", + "", + ""), + OUT_OF_RANGE_COLUMN_VALUE_FOR_BIGINT( + Category.USER_ERROR, + "0063", + "This column value is out of range for BigInt. Value: %s", + "", + ""), + KEY_BUILD_ERROR_UNSUPPORTED_TYPE( + Category.USER_ERROR, "0064", "This type is not supported. Name: %s, Type: %s", "", ""), + STORAGE_NOT_FOUND(Category.USER_ERROR, "0065", "Storage '%s' is not found", "", ""), + TRANSACTION_MANAGER_NOT_FOUND( + Category.USER_ERROR, "0066", "Transaction manager '%s' is not found", "", ""), + CASSANDRA_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED( + Category.USER_ERROR, + "0067", + "Cross-partition scan with filtering or ordering is not supported in Cassandra", + "", + ""), + GET_OPERATION_USED_FOR_NON_EXACT_MATCH_SELECTION( + Category.USER_ERROR, + "0068", + "Please use scan() for non-exact match selection. Operation: %s", + "", + ""), + CASSANDRA_IMPORT_NOT_SUPPORTED( + Category.USER_ERROR, + "0069", + "Import-related functionality is not supported in Cassandra", + "", + ""), + CASSANDRA_NETWORK_STRATEGY_NOT_FOUND( + Category.USER_ERROR, "0070", "The %s network strategy does not exist", "", ""), + INVALID_CONTACT_PORT( + Category.USER_ERROR, + "0071", + "The property 'scalar.db.contact_port' must be greater than or equal to zero", + "", + ""), + COSMOS_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED( + Category.USER_ERROR, + "0072", + "Cross-partition scan with filtering or ordering is not supported in Cosmos DB", + "", + ""), + COSMOS_CLUSTERING_KEY_BLOB_TYPE_NOT_SUPPORTED( + Category.USER_ERROR, + "0073", + "The BLOB type is not supported for clustering keys in Cosmos DB. Column: %s", + "", + ""), + COSMOS_IMPORT_NOT_SUPPORTED( + Category.USER_ERROR, + "0074", + "Import-related functionality is not supported in Cosmos DB", + "", + ""), + INVALID_CONTACT_POINTS( + Category.USER_ERROR, + "0075", + "The property 'scalar.db.contact_points' must not be empty", + "", + ""), + COSMOS_CONDITION_OPERATION_NOT_SUPPORTED_FOR_BLOB_TYPE( + Category.USER_ERROR, + "0076", + "Cosmos DB supports only EQ, NE, IS_NULL, and IS_NOT_NULL operations for the BLOB type in conditions. Mutation: %s", + "", + ""), + INVALID_CONSISTENCY_LEVEL( + Category.USER_ERROR, + "0077", + "The specified consistency level is not supported. Consistency level: %s", + "", + ""), + DYNAMO_ENCODER_0X00_BYTES_NOT_ACCEPTED_IN_BLOB_VALUES_IN_DESC_ORDER( + Category.USER_ERROR, + "0078", + "0x00 bytes are not accepted in BLOB values in DESC order", + "", + ""), + DYNAMO_ENCODER_CANNOT_ENCODE_TEXT_VALUE_CONTAINING_0X0000( + Category.USER_ERROR, "0079", "Cannot encode a Text value that contains '\\u0000'", "", ""), + DYNAMO_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED( + Category.USER_ERROR, + "0080", + "Cross-partition scan with filtering or ordering is not supported in DynamoDB", + "", + ""), + DYNAMO_INDEX_COLUMN_CANNOT_BE_SET_TO_NULL_OR_EMPTY( + Category.USER_ERROR, + "0081", + "An index column cannot be set to null or an empty value for Text or Blob in DynamoDB. Operation: %s", + "", + ""), + DYNAMO_CONDITION_OPERATION_NOT_SUPPORTED_FOR_BOOLEAN_TYPE( + Category.USER_ERROR, + "0082", + "DynamoDB supports only EQ, NE, IS_NULL, and IS_NOT_NULL operations for the BOOLEAN type in conditions. Mutation: %s", + "", + ""), + MULTI_STORAGE_NESTED_MULTI_STORAGE_DEFINITION_NOT_SUPPORTED( + Category.USER_ERROR, + "0083", + "Nested multi-storage definitions are not supported. Storage: %s", + "", + ""), + MULTI_STORAGE_STORAGE_NOT_FOUND( + Category.USER_ERROR, "0084", "Storage not found. Storage: %s", "", ""), + JDBC_NAMESPACE_NAME_NOT_ACCEPTABLE( + Category.USER_ERROR, "0085", "The namespace name is not acceptable. Namespace: %s", "", ""), + JDBC_TABLE_NAME_NOT_ACCEPTABLE( + Category.USER_ERROR, "0086", "The table name is not acceptable. Table: %s", "", ""), + JDBC_IMPORT_NOT_SUPPORTED( + Category.USER_ERROR, + "0087", + "Importing tables is not allowed in the RDB engine. RDB engine: %s", + "", + ""), + JDBC_IMPORT_TABLE_WITHOUT_PRIMARY_KEY( + Category.USER_ERROR, "0088", "The %s table must have a primary key", "", ""), + JDBC_RDB_ENGINE_NOT_SUPPORTED( + Category.USER_ERROR, + "0089", + "The RDB engine is not supported. JDBC connection URL: %s", + "", + ""), + JDBC_IMPORT_DATA_TYPE_WITH_SIZE_NOT_SUPPORTED( + Category.USER_ERROR, "0090", "Data type %s(%d) is not supported: %s", "", ""), + JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED( + Category.USER_ERROR, "0091", "Data type %s is not supported: %s", "", ""), + JDBC_TRANSACTION_GETTING_TRANSACTION_STATE_NOT_SUPPORTED( + Category.USER_ERROR, + "0092", + "Getting a transaction state is not supported in JDBC transactions", + "", + ""), + JDBC_TRANSACTION_ROLLING_BACK_TRANSACTION_NOT_SUPPORTED( + Category.USER_ERROR, + "0093", + "Rolling back a transaction is not supported in JDBC transactions", + "", + ""), + CONSENSUS_COMMIT_COORDINATOR_TABLES_ALREADY_EXIST( + Category.USER_ERROR, "0094", "Coordinator tables already exist", "", ""), + CONSENSUS_COMMIT_COORDINATOR_TABLES_NOT_FOUND( + Category.USER_ERROR, "0095", "Coordinator tables do not exist", "", ""), + CONSENSUS_COMMIT_COORDINATOR_NAMESPACE_SPECIFIED( + Category.USER_ERROR, + "0096", + "The namespace %s is reserved. Any operations on this namespace are not allowed", + "", + ""), + CONSENSUS_COMMIT_MUTATING_TRANSACTION_METADATA_COLUMNS_NOT_ALLOWED( + Category.USER_ERROR, + "0097", + "Mutating transaction metadata columns is not allowed. Table: %s; Column: %s", + "", + ""), + CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_ON_PUT( + Category.USER_ERROR, "0098", "A %s condition is not allowed on Put operations", "", ""), + CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_ON_DELETE( + Category.USER_ERROR, "0099", "A %s condition is not allowed on Delete operations", "", ""), + CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_TO_TARGET_TRANSACTION_METADATA_COLUMNS( + Category.USER_ERROR, + "0100", + "The condition is not allowed to target transaction metadata columns. Column: %s", + "", + ""), + CONSENSUS_COMMIT_COLUMN_RESERVED_AS_TRANSACTION_METADATA( + Category.USER_ERROR, "0101", "Column '%s' is reserved as transaction metadata", "", ""), + CONSENSUS_COMMIT_BEFORE_PREFIXED_COLUMN_FOR_NON_PRIMARY_KEY_RESERVED_AS_TRANSACTION_METADATA( + Category.USER_ERROR, + "0102", + "Non-primary key columns with the 'before_' prefix, '%s', are reserved as transaction metadata", + "", + ""), + CONSENSUS_COMMIT_PUT_CANNOT_HAVE_CONDITION_WHEN_TARGET_RECORD_UNREAD_AND_IMPLICIT_PRE_READ_DISABLED( + Category.USER_ERROR, + "0103", + "Put cannot have a condition when the target record is unread and implicit pre-read is disabled." + + " Please read the target record beforehand or enable implicit pre-read: %s", + "", + ""), + CONSENSUS_COMMIT_WRITING_ALREADY_DELETED_DATA_NOT_ALLOWED( + Category.USER_ERROR, "0104", "Writing already-deleted data is not allowed", "", ""), + CONSENSUS_COMMIT_GETTING_DATA_NEITHER_IN_READ_SET_NOR_DELETE_SET_NOT_ALLOWED( + Category.USER_ERROR, + "0105", + "Getting data neither in the read set nor the delete set is not allowed", + "", + ""), + CONSENSUS_COMMIT_READING_ALREADY_WRITTEN_DATA_NOT_ALLOWED( + Category.USER_ERROR, "0106", "Reading already-written data is not allowed", "", ""), + CONSENSUS_COMMIT_TRANSACTION_NOT_VALIDATED_IN_EXTRA_READ( + Category.USER_ERROR, + "0107", + "The transaction is not validated." + + " When using the EXTRA_READ serializable strategy, you need to call validate()" + + " before calling commit()", + "", + ""), + DYNAMO_BATCH_SIZE_EXCEEDED( + Category.USER_ERROR, "0108", "DynamoDB cannot batch more than 100 mutations at once", "", ""), + + // + // Errors for the concurrency error category + // + NO_MUTATION_APPLIED(Category.CONCURRENCY_ERROR, "0000", "No mutation was applied", "", ""), + CASSANDRA_LOGGING_FAILED_IN_BATCH( + Category.CONCURRENCY_ERROR, "0001", "Logging failed in the batch", "", ""), + CASSANDRA_OPERATION_FAILED_IN_BATCH( + Category.CONCURRENCY_ERROR, "0002", "The operation failed in the batch with type %s", "", ""), + CASSANDRA_ERROR_OCCURRED_IN_BATCH( + Category.CONCURRENCY_ERROR, "0003", "An error occurred in the batch. Details: %s", "", ""), + CASSANDRA_WRITE_TIMEOUT_IN_PAXOS_PHASE_IN_MUTATION( + Category.CONCURRENCY_ERROR, "0004", "A Paxos phase in the CAS operation failed", "", ""), + CASSANDRA_WRITE_TIMEOUT_IN_LEARN_PHASE_IN_MUTATION( + Category.CONCURRENCY_ERROR, "0005", "The learn phase in the CAS operation failed", "", ""), + CASSANDRA_WRITE_TIMEOUT_SIMPLE_WRITE_OPERATION_FAILED_IN_MUTATION( + Category.CONCURRENCY_ERROR, "0006", "A simple write operation failed", "", ""), + CASSANDRA_ERROR_OCCURRED_IN_MUTATION( + Category.CONCURRENCY_ERROR, "0007", "An error occurred in the mutation. Details: %s", "", ""), + COSMOS_RETRY_WITH_ERROR_OCCURRED_IN_MUTATION( + Category.CONCURRENCY_ERROR, + "0008", + "A RetryWith error occurred in the mutation. Details: %s", + "", + ""), + DYNAMO_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION( + Category.CONCURRENCY_ERROR, + "0009", + "A transaction conflict occurred in the mutation. Details: %s", + "", + ""), + JDBC_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION( + Category.CONCURRENCY_ERROR, + "0010", + "A transaction conflict occurred in the mutation. Details: %s", + "", + ""), + JDBC_TRANSACTION_CONFLICT_OCCURRED( + Category.CONCURRENCY_ERROR, + "0011", + "A conflict occurred. Please try restarting the transaction", + "", + ""), + JDBC_TRANSACTION_CONDITION_NOT_SATISFIED( + Category.CONCURRENCY_ERROR, + "0012", + "The %s condition of the %s operation is not satisfied. Targeting column(s): %s", + "", + ""), + CONSENSUS_COMMIT_PREPARING_RECORD_EXISTS( + Category.CONCURRENCY_ERROR, "0013", "The record being prepared already exists", "", ""), + CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHEN_PREPARING_RECORDS( + Category.CONCURRENCY_ERROR, "0014", "A conflict occurred when preparing records", "", ""), + CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHEN_COMMITTING_STATE( + Category.CONCURRENCY_ERROR, + "0015", + "The committing state in the coordinator failed. The transaction has been aborted", + "", + ""), + CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHILE_IMPLICIT_PRE_READ( + Category.CONCURRENCY_ERROR, "0016", "A conflict occurred during implicit pre-read", "", ""), + CONSENSUS_COMMIT_READ_UNCOMMITTED_RECORD( + Category.CONCURRENCY_ERROR, "0017", "This record needs to be recovered", "", ""), + CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED_BECAUSE_RECORD_NOT_EXISTS( + Category.CONCURRENCY_ERROR, + "0018", + "The record does not exist, so the %s condition is not satisfied", + "", + ""), + CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED_BECAUSE_RECORD_EXISTS( + Category.CONCURRENCY_ERROR, + "0019", + "The record exists, so the %s condition is not satisfied", + "", + ""), + CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED( + Category.CONCURRENCY_ERROR, + "0020", + "The condition on the column '%s' is not satisfied", + "", + ""), + CONSENSUS_COMMIT_READING_EMPTY_RECORDS_IN_EXTRA_WRITE( + Category.CONCURRENCY_ERROR, + "0021", + "Reading empty records might cause a write skew anomaly, so the transaction has been aborted for safety purposes", + "", + ""), + CONSENSUS_COMMIT_ANTI_DEPENDENCY_FOUND_IN_EXTRA_READ( + Category.CONCURRENCY_ERROR, + "0022", + "An anti-dependency was found. The transaction has been aborted", + "", + ""), + + // + // Errors for the internal error category + // + CREATING_NAMESPACE_FAILED( + Category.INTERNAL_ERROR, "0000", "Creating the namespace failed. Namespace: %s", "", ""), + DROPPING_NAMESPACE_FAILED( + Category.INTERNAL_ERROR, "0001", "Dropping the namespace failed. Namespace: %s", "", ""), + CREATING_TABLE_FAILED( + Category.INTERNAL_ERROR, "0002", "Creating the table failed. Table: %s", "", ""), + DROPPING_TABLE_FAILED( + Category.INTERNAL_ERROR, "0003", "Dropping the table failed. Table: %s", "", ""), + TRUNCATING_TABLE_FAILED( + Category.INTERNAL_ERROR, "0004", "Truncating the table failed. Table: %s", "", ""), + CREATING_INDEX_FAILED( + Category.INTERNAL_ERROR, "0005", "Creating the index failed. Table: %s, Column: %s", "", ""), + DROPPING_INDEX_FAILED( + Category.INTERNAL_ERROR, "0006", "Dropping the index failed. Table: %s, Column: %s", "", ""), + GETTING_TABLE_METADATA_FAILED( + Category.INTERNAL_ERROR, "0007", "Getting the table metadata failed. Table: %s", "", ""), + GETTING_TABLE_NAMES_IN_NAMESPACE_FAILED( + Category.INTERNAL_ERROR, + "0008", + "Getting the table names in the namespace failed. Namespace: %s", + "", + ""), + CHECKING_NAMESPACE_EXISTENCE_FAILED( + Category.INTERNAL_ERROR, + "0009", + "Checking the namespace existence failed. Namespace: %s", + "", + ""), + CHECKING_TABLE_EXISTENCE_FAILED( + Category.INTERNAL_ERROR, "0010", "Checking the table existence failed. Table: %s", "", ""), + CHECKING_INDEX_EXISTENCE_FAILED( + Category.INTERNAL_ERROR, + "0011", + "Checking the index existence failed. Table: %s; Column: %s", + "", + ""), + REPAIRING_NAMESPACE_FAILED( + Category.INTERNAL_ERROR, "0012", "Repairing the namespace failed. Namespace: %s", "", ""), + REPAIRING_TABLE_FAILED( + Category.INTERNAL_ERROR, "0013", "Repairing the table failed. Table: %s", "", ""), + ADDING_NEW_COLUMN_TO_TABLE_FAILED( + Category.INTERNAL_ERROR, + "0014", + "Adding a new column to the table failed. Table: %s; Column: %s; ColumnType: %s", + "", + ""), + GETTING_NAMESPACE_NAMES_FAILED( + Category.INTERNAL_ERROR, "0015", "Getting the namespace names failed", "", ""), + GETTING_IMPORT_TABLE_METADATA_FAILED( + Category.INTERNAL_ERROR, + "0016", + "Getting the table metadata of the table being imported failed. Table: %s", + "", + ""), + IMPORTING_TABLE_FAILED( + Category.INTERNAL_ERROR, "0017", "Importing the table failed. Table: %s", "", ""), + ADDING_RAW_COLUMN_TO_TABLE_FAILED( + Category.INTERNAL_ERROR, + "0018", + "Adding the raw column to the table failed. Table: %s; Column: %s; ColumnType: %s", + "", + ""), + UPGRADING_SCALAR_DB_ENV_FAILED( + Category.INTERNAL_ERROR, "0019", "Upgrading the ScalarDB environment failed", "", ""), + CASSANDRA_WRITE_TIMEOUT_WITH_OTHER_WRITE_TYPE_IN_MUTATION( + Category.INTERNAL_ERROR, + "0020", + "Something wrong because WriteType is neither CAS nor SIMPLE", + "", + ""), + CASSANDRA_ERROR_OCCURRED_IN_SELECTION( + Category.INTERNAL_ERROR, "0021", "An error occurred in the selection. Details: %s", "", ""), + COSMOS_ERROR_OCCURRED_IN_MUTATION( + Category.INTERNAL_ERROR, "0022", "An error occurred in the mutation. Details: %s", "", ""), + COSMOS_ERROR_OCCURRED_IN_SELECTION( + Category.INTERNAL_ERROR, "0023", "An error occurred in the selection. Details: %s", "", ""), + DYNAMO_ERROR_OCCURRED_IN_MUTATION( + Category.INTERNAL_ERROR, "0024", "An error occurred in the mutation. Details: %s", "", ""), + DYNAMO_ERROR_OCCURRED_IN_SELECTION( + Category.INTERNAL_ERROR, "0025", "An error occurred in the selection. Details: %s", "", ""), + JDBC_ERROR_OCCURRED_IN_MUTATION( + Category.INTERNAL_ERROR, "0026", "An error occurred in the mutation. Details: %s", "", ""), + JDBC_ERROR_OCCURRED_IN_SELECTION( + Category.INTERNAL_ERROR, "0027", "An error occurred in the selection. Details: %s", "", ""), + JDBC_FETCHING_NEXT_RESULT_FAILED( + Category.INTERNAL_ERROR, "0028", "Fetching the next result failed", "", ""), + JDBC_TRANSACTION_ROLLING_BACK_TRANSACTION_FAILED( + Category.INTERNAL_ERROR, "0029", "Rolling back the transaction failed", "", ""), + JDBC_TRANSACTION_COMMITTING_TRANSACTION_FAILED( + Category.INTERNAL_ERROR, "0030", "Committing the transaction failed", "", ""), + JDBC_TRANSACTION_GET_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0031", "The Get operation failed", "", ""), + JDBC_TRANSACTION_SCAN_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0032", "The Scan operation failed", "", ""), + JDBC_TRANSACTION_PUT_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0033", "The Put operation failed", "", ""), + JDBC_TRANSACTION_DELETE_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0034", "The Delete operation failed", "", ""), + JDBC_TRANSACTION_BEGINNING_TRANSACTION_FAILED( + Category.INTERNAL_ERROR, "0035", "Beginning a transaction failed", "", ""), + CONSENSUS_COMMIT_PREPARING_RECORDS_FAILED( + Category.INTERNAL_ERROR, "0036", "Preparing records failed", "", ""), + CONSENSUS_COMMIT_VALIDATION_FAILED(Category.INTERNAL_ERROR, "0037", "Validation failed", "", ""), + CONSENSUS_COMMIT_EXECUTING_IMPLICIT_PRE_READ_FAILED( + Category.INTERNAL_ERROR, "0038", "Executing implicit pre-read failed", "", ""), + CONSENSUS_COMMIT_GET_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0039", "The Get operation failed", "", ""), + CONSENSUS_COMMIT_SCAN_OPERATION_FAILED( + Category.INTERNAL_ERROR, "0040", "The Scan operation failed", "", ""), + CONSENSUS_COMMIT_ROLLBACK_FAILED_BECAUSE_TRANSACTION_ALREADY_COMMITTED( + Category.INTERNAL_ERROR, + "0041", + "Rollback failed because the transaction has already been committed", + "", + ""), + CONSENSUS_COMMIT_ROLLBACK_FAILED(Category.INTERNAL_ERROR, "0042", "Rollback failed", "", ""), + + // + // Errors for the unknown transaction status error category + // + JDBC_TRANSACTION_UNKNOWN_TRANSACTION_STATUS( + Category.UNKNOWN_TRANSACTION_STATUS_ERROR, + "0000", + "Rolling back the transaction failed", + "", + ""), + CONSENSUS_COMMIT_COMMITTING_STATE_FAILED_WITH_NO_MUTATION_EXCEPTION_BUT_COORDINATOR_STATUS_DOES_NOT_EXIST( + Category.UNKNOWN_TRANSACTION_STATUS_ERROR, + "0001", + "Committing state failed with NoMutationException, but the coordinator status does not exist", + "", + ""), + CONSENSUS_COMMIT_CANNOT_GET_STATE( + Category.UNKNOWN_TRANSACTION_STATUS_ERROR, "0002", "The state cannot be retrieved", "", ""), + CONSENSUS_COMMIT_UNKNOWN_COORDINATOR_STATUS( + Category.UNKNOWN_TRANSACTION_STATUS_ERROR, + "0003", + "The coordinator status is unknown", + "", + ""), + CONSENSUS_COMMIT_ABORTING_STATE_FAILED_WITH_NO_MUTATION_EXCEPTION_BUT_COORDINATOR_STATUS_DOES_NOT_EXIST( + Category.UNKNOWN_TRANSACTION_STATUS_ERROR, + "0004", + "Aborting state failed with NoMutationException, but the coordinator status does not exist", + "", + ""), + ; + + private static final String COMPONENT_NAME = "CORE"; + + private final Category category; + private final String id; + private final String message; + private final String cause; + private final String solution; + + CoreError(Category category, String id, String message, String cause, String solution) { + validate(COMPONENT_NAME, category, id, message, cause, solution); + + this.category = category; + this.id = id; + this.message = message; + this.cause = cause; + this.solution = solution; + } + + @Override + public String getComponentName() { + return COMPONENT_NAME; + } + + @Override + public Category getCategory() { + return category; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getCause() { + return cause; + } + + @Override + public String getSolution() { + return solution; + } +} diff --git a/core/src/main/java/com/scalar/db/common/error/ScalarDbError.java b/core/src/main/java/com/scalar/db/common/error/ScalarDbError.java new file mode 100644 index 0000000000..46e2f23af4 --- /dev/null +++ b/core/src/main/java/com/scalar/db/common/error/ScalarDbError.java @@ -0,0 +1,65 @@ +package com.scalar.db.common.error; + +import java.util.Objects; + +public interface ScalarDbError { + + String getComponentName(); + + Category getCategory(); + + String getId(); + + String getMessage(); + + String getCause(); + + String getSolution(); + + // This method validates the error. It is called in the constructor of the enum to ensure that the + // error is valid. + default void validate( + String componentName, + Category category, + String id, + String message, + String cause, + String solution) { + Objects.requireNonNull(componentName, "The component name must not be null."); + Objects.requireNonNull(category, "The category must not be null."); + + Objects.requireNonNull(id, "The id must not be null."); + if (id.length() != 4) { + throw new IllegalArgumentException("The length of the id must be 4"); + } + + Objects.requireNonNull(message, "The message must not be null."); + Objects.requireNonNull(cause, "The cause must not be null."); + Objects.requireNonNull(solution, "The solution must not be null."); + } + + /** + * Builds the error code. The code is built as follows: + * + *

{@code -} + * + * @return the built code + */ + default String buildCode() { + return getComponentName() + "-" + getCategory().getId() + getId(); + } + + /** + * Builds the error message with the given arguments. The message is built as follows: + * + *

{@code -: } + * + * @param args the arguments to be formatted into the message + * @return the formatted message + */ + default String buildMessage(Object... args) { + return buildCode() + + ": " + + (args.length == 0 ? getMessage() : String.format(getMessage(), args)); + } +} diff --git a/core/src/main/java/com/scalar/db/config/ConfigUtils.java b/core/src/main/java/com/scalar/db/config/ConfigUtils.java index 85b8dcfa29..b1ae9bf851 100644 --- a/core/src/main/java/com/scalar/db/config/ConfigUtils.java +++ b/core/src/main/java/com/scalar/db/config/ConfigUtils.java @@ -3,6 +3,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.scalar.db.common.error.CoreError; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; @@ -58,7 +59,7 @@ public static int getInt(Properties properties, String name, int defaultValue) { return Integer.parseInt(value); } catch (NumberFormatException ignored) { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a number. value: " + value); + CoreError.CONFIG_UTILS_INVALID_NUMBER_FORMAT.buildMessage(name, value)); } } @@ -72,7 +73,7 @@ public static Integer getInt(Properties properties, String name, @Nullable Integ return Integer.parseInt(value); } catch (NumberFormatException ignored) { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a number. value: " + value); + CoreError.CONFIG_UTILS_INVALID_NUMBER_FORMAT.buildMessage(name, value)); } } @@ -85,7 +86,7 @@ public static long getLong(Properties properties, String name, long defaultValue return Long.parseLong(value); } catch (NumberFormatException ignored) { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a number. value: " + value); + CoreError.CONFIG_UTILS_INVALID_NUMBER_FORMAT.buildMessage(name, value)); } } @@ -99,7 +100,7 @@ public static Long getLong(Properties properties, String name, @Nullable Long de return Long.parseLong(value); } catch (NumberFormatException ignored) { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a number. value: " + value); + CoreError.CONFIG_UTILS_INVALID_NUMBER_FORMAT.buildMessage(name, value)); } } @@ -113,7 +114,7 @@ public static boolean getBoolean(Properties properties, String name, boolean def return Boolean.parseBoolean(value); } else { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a boolean value. value: " + value); + CoreError.CONFIG_UTILS_INVALID_BOOLEAN_FORMAT.buildMessage(name, value)); } } @@ -129,7 +130,7 @@ public static Boolean getBoolean( return Boolean.parseBoolean(value); } else { throw new IllegalArgumentException( - "The specified value of '" + name + "' is not a boolean value. value: " + value); + CoreError.CONFIG_UTILS_INVALID_BOOLEAN_FORMAT.buildMessage(name, value)); } } @@ -153,7 +154,8 @@ public static String getStringFromFilePath( try { return new String(Files.readAllBytes(new File(path).toPath()), StandardCharsets.UTF_8); } catch (IOException e) { - throw new UncheckedIOException(e); + throw new UncheckedIOException( + CoreError.CONFIG_UTILS_READING_FILE_FAILED.buildMessage(path), e); } } diff --git a/core/src/main/java/com/scalar/db/config/DatabaseConfig.java b/core/src/main/java/com/scalar/db/config/DatabaseConfig.java index 336cd69f33..d72a331552 100644 --- a/core/src/main/java/com/scalar/db/config/DatabaseConfig.java +++ b/core/src/main/java/com/scalar/db/config/DatabaseConfig.java @@ -8,6 +8,7 @@ import static com.scalar.db.config.ConfigUtils.getStringArray; import com.google.common.collect.ImmutableList; +import com.scalar.db.common.error.CoreError; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.File; import java.io.FileInputStream; @@ -97,7 +98,7 @@ protected void load() { contactPoints = ImmutableList.copyOf(getStringArray(getProperties(), CONTACT_POINTS, new String[0])); contactPort = getInt(getProperties(), CONTACT_PORT, 0); - checkArgument(contactPort >= 0); + checkArgument(contactPort >= 0, CoreError.INVALID_CONTACT_PORT.buildMessage()); username = getString(getProperties(), USERNAME, null); password = getString(getProperties(), PASSWORD, null); transactionManager = getString(getProperties(), TRANSACTION_MANAGER, "consensus-commit"); @@ -115,8 +116,9 @@ protected void load() { if (!crossPartitionScanEnabled && (crossPartitionScanFilteringEnabled || crossPartitionScanOrderingEnabled)) { throw new IllegalArgumentException( - CROSS_PARTITION_SCAN - + " must be enabled to use cross-partition scan with filtering or ordering"); + CoreError + .CROSS_PARTITION_SCAN_MUST_BE_ENABLED_TO_USE_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING + .buildMessage()); } systemNamespaceName = diff --git a/core/src/main/java/com/scalar/db/exception/transaction/TransactionException.java b/core/src/main/java/com/scalar/db/exception/transaction/TransactionException.java index befd4fcee9..43175ce097 100644 --- a/core/src/main/java/com/scalar/db/exception/transaction/TransactionException.java +++ b/core/src/main/java/com/scalar/db/exception/transaction/TransactionException.java @@ -66,7 +66,7 @@ private static String addTransactionIdToMessage(String message, @Nullable String return message; } - String suffix = ". transaction ID: " + transactionId; + String suffix = ". Transaction ID: " + transactionId; // To avoid a duplicated transaction ID, check if the message already has a transaction ID if (!message.endsWith(suffix)) { diff --git a/core/src/main/java/com/scalar/db/io/BigIntColumn.java b/core/src/main/java/com/scalar/db/io/BigIntColumn.java index bcbf23e561..eb61d6715f 100644 --- a/core/src/main/java/com/scalar/db/io/BigIntColumn.java +++ b/core/src/main/java/com/scalar/db/io/BigIntColumn.java @@ -2,6 +2,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ComparisonChain; +import com.scalar.db.common.error.CoreError; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -32,7 +33,8 @@ private BigIntColumn(String name) { private BigIntColumn(String name, long value, boolean hasNullValue) { this.name = Objects.requireNonNull(name); if (value < MIN_VALUE || value > MAX_VALUE) { - throw new IllegalArgumentException("Out of range column value for BigInt. value: " + value); + throw new IllegalArgumentException( + CoreError.OUT_OF_RANGE_COLUMN_VALUE_FOR_BIGINT.buildMessage(value)); } this.value = value; this.hasNullValue = hasNullValue; diff --git a/core/src/main/java/com/scalar/db/io/Key.java b/core/src/main/java/com/scalar/db/io/Key.java index f19b3dd59c..27e5696610 100644 --- a/core/src/main/java/com/scalar/db/io/Key.java +++ b/core/src/main/java/com/scalar/db/io/Key.java @@ -5,6 +5,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; +import com.scalar.db.common.error.CoreError; import com.scalar.db.util.ScalarDbUtils; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -275,7 +276,8 @@ private Column toColumn(String name, Object value) { return BlobColumn.of(name, (ByteBuffer) value); } else { throw new IllegalArgumentException( - "Unsupported type, name: " + name + ", type: " + value.getClass().getName()); + CoreError.KEY_BUILD_ERROR_UNSUPPORTED_TYPE.buildMessage( + name, value.getClass().getName())); } } diff --git a/core/src/main/java/com/scalar/db/service/ProviderManager.java b/core/src/main/java/com/scalar/db/service/ProviderManager.java index 6a133c21d4..1889e30685 100644 --- a/core/src/main/java/com/scalar/db/service/ProviderManager.java +++ b/core/src/main/java/com/scalar/db/service/ProviderManager.java @@ -8,6 +8,7 @@ import com.scalar.db.api.DistributedTransactionManager; import com.scalar.db.api.DistributedTransactionProvider; import com.scalar.db.api.TwoPhaseCommitTransactionManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import java.util.Locale; import java.util.Map; @@ -79,7 +80,7 @@ public static DistributedStorageAdmin createDistributedStorageAdmin(DatabaseConf private static DistributedStorageProvider getDistributedStorageProvider(String name) { String lowerCaseName = name.toLowerCase(Locale.ROOT); if (!DISTRIBUTED_STORAGE_PROVIDERS.containsKey(lowerCaseName)) { - throw new IllegalArgumentException("Storage '" + name + "' is not found"); + throw new IllegalArgumentException(CoreError.STORAGE_NOT_FOUND.buildMessage(name)); } return DISTRIBUTED_STORAGE_PROVIDERS.get(lowerCaseName); } @@ -123,7 +124,8 @@ public static TwoPhaseCommitTransactionManager createTwoPhaseCommitTransactionMa private static DistributedTransactionProvider getDistributedTransactionProvider(String name) { String lowerCaseName = name.toLowerCase(Locale.ROOT); if (!DISTRIBUTED_TRANSACTION_PROVIDERS.containsKey(lowerCaseName)) { - throw new IllegalArgumentException("Transaction manager '" + name + "' is not found"); + throw new IllegalArgumentException( + CoreError.TRANSACTION_MANAGER_NOT_FOUND.buildMessage(name)); } return DISTRIBUTED_TRANSACTION_PROVIDERS.get(lowerCaseName); } diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/BatchHandler.java b/core/src/main/java/com/scalar/db/storage/cassandra/BatchHandler.java index e221dafc54..b604e5bf4c 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/BatchHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/BatchHandler.java @@ -10,6 +10,7 @@ import com.datastax.driver.core.exceptions.WriteTimeoutException; import com.google.common.annotations.VisibleForTesting; import com.scalar.db.api.Mutation; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -57,22 +58,24 @@ public void handle(List mutations) ResultSet results = execute(mutations); // it's for conditional update. non-conditional update always return true if (!results.wasApplied()) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage()); } } catch (WriteTimeoutException e) { logger.warn("Write timeout happened during batch mutate operation", e); WriteType writeType = e.getWriteType(); if (writeType == WriteType.BATCH_LOG) { - throw new RetriableExecutionException("Logging failed in the batch", e); + throw new RetriableExecutionException( + CoreError.CASSANDRA_LOGGING_FAILED_IN_BATCH.buildMessage(), e); } else if (writeType == WriteType.BATCH) { logger.warn("Logging succeeded, but mutations in the batch partially failed", e); } else { throw new RetriableExecutionException( - "operation failed in the batch with type " + writeType, e); + CoreError.CASSANDRA_OPERATION_FAILED_IN_BATCH.buildMessage(writeType), e); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); - throw new RetriableExecutionException(e.getMessage(), e); + throw new RetriableExecutionException( + CoreError.CASSANDRA_ERROR_OCCURRED_IN_BATCH.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/Cassandra.java b/core/src/main/java/com/scalar/db/storage/cassandra/Cassandra.java index 439f64d808..7f82468800 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/Cassandra.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/Cassandra.java @@ -18,6 +18,7 @@ import com.scalar.db.common.AbstractDistributedStorage; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import java.util.List; @@ -48,7 +49,8 @@ public Cassandra(DatabaseConfig config) { if (config.isCrossPartitionScanFilteringEnabled() || config.isCrossPartitionScanOrderingEnabled()) { throw new IllegalArgumentException( - "Cross-partition scan with filtering or ordering is not supported in Cassandra"); + CoreError.CASSANDRA_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED + .buildMessage()); } clusterManager = new ClusterManager(config); @@ -105,7 +107,8 @@ public Optional get(Get get) throws ExecutionException { } Row next = resultSet.one(); if (next != null) { - throw new IllegalArgumentException("Please use scan() for non-exact match selection"); + throw new IllegalArgumentException( + CoreError.GET_OPERATION_USED_FOR_NON_EXACT_MATCH_SELECTION.buildMessage(get)); } return Optional.of( new ResultInterpreter(get.getProjections(), metadataManager.getTableMetadata(get)) @@ -151,7 +154,7 @@ public void delete(List deletes) throws ExecutionException { @Override public void mutate(List mutations) throws ExecutionException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); if (mutations.size() == 1) { Mutation mutation = mutations.get(0); if (mutation instanceof Put) { diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/CassandraAdmin.java b/core/src/main/java/com/scalar/db/storage/cassandra/CassandraAdmin.java index 8eb7687f14..94272f18f2 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/CassandraAdmin.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/CassandraAdmin.java @@ -22,6 +22,7 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.Scan.Ordering.Order; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -270,20 +271,20 @@ private TableMetadata createTableMetadata(com.datastax.driver.core.TableMetadata @Override public TableMetadata getImportTableMetadata(String namespace, String table) { throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cassandra"); + CoreError.CASSANDRA_IMPORT_NOT_SUPPORTED.buildMessage()); } @Override public void addRawColumnToTable( String namespace, String table, String columnName, DataType columnType) { throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cassandra"); + CoreError.CASSANDRA_IMPORT_NOT_SUPPORTED.buildMessage()); } @Override public void importTable(String namespace, String table, Map options) { throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cassandra"); + CoreError.CASSANDRA_IMPORT_NOT_SUPPORTED.buildMessage()); } private Scan.Ordering.Order convertOrder(ClusteringOrder clusteringOrder) { @@ -636,7 +637,7 @@ public static ReplicationStrategy fromString(String text) { } } throw new IllegalArgumentException( - String.format("The %s network strategy does not exist", text)); + CoreError.CASSANDRA_NETWORK_STRATEGY_NOT_FOUND.buildMessage(text)); } @Override diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/MutateStatementHandler.java b/core/src/main/java/com/scalar/db/storage/cassandra/MutateStatementHandler.java index ee7279e479..a5d99a2c70 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/MutateStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/MutateStatementHandler.java @@ -9,6 +9,7 @@ import com.datastax.driver.core.querybuilder.BuiltStatement; import com.scalar.db.api.Mutation; import com.scalar.db.api.Operation; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -43,30 +44,36 @@ public ResultSet handle(Operation operation) throws ExecutionException { Mutation mutation = (Mutation) operation; if (mutation.getCondition().isPresent() && !results.one().getBool(0)) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage()); } return results; - } catch (WriteTimeoutException e) { logger.warn("Write timeout happened during mutate operation", e); if (e.getWriteType() == WriteType.CAS) { // retry needs to be done if applications need to do the operation exactly - throw new RetriableExecutionException("Paxos phase in CAS operation failed", e); + throw new RetriableExecutionException( + CoreError.CASSANDRA_WRITE_TIMEOUT_IN_PAXOS_PHASE_IN_MUTATION.buildMessage(), e); } else if (e.getWriteType() == WriteType.SIMPLE) { Mutation mutation = (Mutation) operation; if (mutation.getCondition().isPresent()) { // learn phase needs to be repaired (by re-reading) - throw new ReadRepairableExecutionException("Learn phase in CAS operation failed", e); + throw new ReadRepairableExecutionException( + CoreError.CASSANDRA_WRITE_TIMEOUT_IN_LEARN_PHASE_IN_MUTATION.buildMessage(), e); } else { // retry needs to be done if applications need to do the operation exactly - throw new RetriableExecutionException("Simple write operation failed", e); + throw new RetriableExecutionException( + CoreError.CASSANDRA_WRITE_TIMEOUT_SIMPLE_WRITE_OPERATION_FAILED_IN_MUTATION + .buildMessage(), + e); } } else { - throw new ExecutionException("Something wrong because it is neither CAS nor SIMPLE", e); + throw new ExecutionException( + CoreError.CASSANDRA_WRITE_TIMEOUT_WITH_OTHER_WRITE_TYPE_IN_MUTATION.buildMessage(), e); } } catch (RuntimeException e) { logger.warn(e.getMessage(), e); - throw new RetriableExecutionException(e.getMessage(), e); + throw new RetriableExecutionException( + CoreError.CASSANDRA_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/SelectStatementHandler.java b/core/src/main/java/com/scalar/db/storage/cassandra/SelectStatementHandler.java index 52df8a43c9..c9a74e8ac3 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/SelectStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/SelectStatementHandler.java @@ -21,6 +21,8 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.ScanAll; import com.scalar.db.api.Selection; +import com.scalar.db.common.error.CoreError; +import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.Column; import com.scalar.db.io.Key; import com.scalar.db.io.Value; @@ -53,6 +55,18 @@ public SelectStatementHandler(Session session) { super(session); } + @Override + @Nonnull + public ResultSet handle(Operation operation) throws ExecutionException { + try { + return handleInternal(operation); + } catch (RuntimeException e) { + logger.error(e.getMessage(), e); + throw new ExecutionException( + CoreError.CASSANDRA_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); + } + } + @Override @Nonnull protected PreparedStatement prepare(Operation operation) { diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandler.java b/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandler.java index b594f43a92..e2b5fe2862 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandler.java @@ -43,14 +43,7 @@ protected StatementHandler(Session session) { * @throws ExecutionException if the execution fails */ @Nonnull - public ResultSet handle(Operation operation) throws ExecutionException { - try { - return handleInternal(operation); - } catch (RuntimeException e) { - logger.error(e.getMessage(), e); - throw new ExecutionException(e.getMessage(), e); - } - } + public abstract ResultSet handle(Operation operation) throws ExecutionException; /** * Executes the specified {@code Operation} diff --git a/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandlerManager.java b/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandlerManager.java index fe8b332907..c1b598ecbb 100644 --- a/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandlerManager.java +++ b/core/src/main/java/com/scalar/db/storage/cassandra/StatementHandlerManager.java @@ -70,7 +70,7 @@ public StatementHandler get(Operation operation) { return delete(); } // never comes here usually - throw new IllegalArgumentException("Unexpected operation was given"); + throw new AssertionError("Unexpected operation was given"); } @Nonnull @@ -110,7 +110,7 @@ public Builder delete(DeleteStatementHandler delete) { public StatementHandlerManager build() { if (select == null || insert == null || update == null || delete == null) { - throw new IllegalArgumentException("Please set all the statement handlers"); + throw new IllegalStateException("Please set all the statement handlers"); } return new StatementHandlerManager(this); } diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/BatchHandler.java b/core/src/main/java/com/scalar/db/storage/cosmos/BatchHandler.java index 78ab986c83..4981297658 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/BatchHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/BatchHandler.java @@ -5,6 +5,7 @@ import com.scalar.db.api.Mutation; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -52,6 +53,9 @@ public void handle(List mutations) throws ExecutionException executeStoredProcedure(mutations, tableMetadata); } catch (CosmosException e) { throwException(e); + } catch (RuntimeException e) { + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } @@ -88,11 +92,16 @@ private void throwException(CosmosException exception) throws ExecutionException int statusCode = exception.getSubStatusCode(); if (statusCode == CosmosErrorCode.PRECONDITION_FAILED.get()) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage(), exception); } else if (statusCode == CosmosErrorCode.RETRY_WITH.get()) { - throw new RetriableExecutionException(exception.getMessage(), exception); + throw new RetriableExecutionException( + CoreError.COSMOS_RETRY_WITH_ERROR_OCCURRED_IN_MUTATION.buildMessage( + exception.getMessage()), + exception); } - throw new ExecutionException(exception.getMessage(), exception); + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_MUTATION.buildMessage(exception.getMessage()), + exception); } } diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/Cosmos.java b/core/src/main/java/com/scalar/db/storage/cosmos/Cosmos.java index 18b53721e0..d833eaeed7 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/Cosmos.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/Cosmos.java @@ -16,6 +16,7 @@ import com.scalar.db.common.AbstractDistributedStorage; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import java.util.List; @@ -48,7 +49,8 @@ public Cosmos(DatabaseConfig databaseConfig) { if (databaseConfig.isCrossPartitionScanFilteringEnabled() || databaseConfig.isCrossPartitionScanOrderingEnabled()) { throw new IllegalArgumentException( - "Cross-partition scan with filtering or ordering is not supported in Cosmos DB"); + CoreError.COSMOS_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED + .buildMessage()); } CosmosConfig config = new CosmosConfig(databaseConfig); @@ -99,7 +101,8 @@ public Optional get(Get get) throws ExecutionException { Scanner scanner = selectStatementHandler.handle(get); Optional ret = scanner.one(); if (scanner.one().isPresent()) { - throw new IllegalArgumentException("Please use scan() for non-exact match selection"); + throw new IllegalArgumentException( + CoreError.GET_OPERATION_USED_FOR_NON_EXACT_MATCH_SELECTION.buildMessage(get)); } return ret; @@ -141,7 +144,7 @@ public void delete(List deletes) throws ExecutionException { @Override public void mutate(List mutations) throws ExecutionException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED); if (mutations.size() == 1) { Mutation mutation = mutations.get(0); if (mutation instanceof Put) { diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java index feec345452..8382abe010 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java @@ -25,6 +25,7 @@ import com.scalar.db.api.DistributedStorageAdmin; import com.scalar.db.api.Scan.Ordering.Order; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -106,7 +107,8 @@ private void checkMetadata(TableMetadata metadata) { for (String clusteringKeyName : metadata.getClusteringKeyNames()) { if (metadata.getColumnDataType(clusteringKeyName) == DataType.BLOB) { throw new IllegalArgumentException( - "Currently, BLOB type is not supported for clustering keys in Cosmos DB"); + CoreError.COSMOS_CLUSTERING_KEY_BLOB_TYPE_NOT_SUPPORTED.buildMessage( + clusteringKeyName)); } } } @@ -627,21 +629,18 @@ columnName, getFullTableName(namespace, table)), @Override public TableMetadata getImportTableMetadata(String namespace, String table) { - throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cosmos DB"); + throw new UnsupportedOperationException(CoreError.COSMOS_IMPORT_NOT_SUPPORTED.buildMessage()); } @Override public void addRawColumnToTable( String namespace, String table, String columnName, DataType columnType) { - throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cosmos DB"); + throw new UnsupportedOperationException(CoreError.COSMOS_IMPORT_NOT_SUPPORTED.buildMessage()); } @Override public void importTable(String namespace, String table, Map options) { - throw new UnsupportedOperationException( - "Import-related functionality is not supported in Cosmos DB"); + throw new UnsupportedOperationException(CoreError.COSMOS_IMPORT_NOT_SUPPORTED.buildMessage()); } @Override @@ -687,7 +686,7 @@ public void upgrade(Map options) throws ExecutionException { namespaceName -> getNamespacesContainer().upsertItem(new CosmosNamespace(namespaceName))); } catch (RuntimeException e) { - throw new ExecutionException("Upgrading the ScalarDB environemnt failed", e); + throw new ExecutionException("Upgrading the ScalarDB environment failed", e); } } diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosConfig.java b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosConfig.java index b835b1cce4..2ccccaf308 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosConfig.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosConfig.java @@ -2,6 +2,7 @@ import static com.scalar.db.config.ConfigUtils.getString; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import java.util.Optional; import javax.annotation.Nullable; @@ -35,7 +36,7 @@ public CosmosConfig(DatabaseConfig databaseConfig) { } if (databaseConfig.getContactPoints().isEmpty()) { - throw new IllegalArgumentException(DatabaseConfig.CONTACT_POINTS + " is empty"); + throw new IllegalArgumentException(CoreError.INVALID_CONTACT_POINTS.buildMessage()); } endpoint = databaseConfig.getContactPoints().get(0); key = databaseConfig.getPassword().orElse(null); diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosOperationChecker.java b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosOperationChecker.java index 1b0b32eff5..d8f8669475 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosOperationChecker.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosOperationChecker.java @@ -7,6 +7,7 @@ import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -42,7 +43,8 @@ private void checkCondition(Mutation mutation, TableMetadata metadata) { && expression.getOperator() != ConditionalExpression.Operator.IS_NULL && expression.getOperator() != ConditionalExpression.Operator.IS_NOT_NULL) { throw new IllegalArgumentException( - "Cosmos DB only supports EQ, NE, IS_NULL, and IS_NOT_NULL operations for BLOB type in conditions"); + CoreError.COSMOS_CONDITION_OPERATION_NOT_SUPPORTED_FOR_BLOB_TYPE.buildMessage( + mutation)); } } } diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosUtils.java b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosUtils.java index 4fb1bf208a..067d0b0c8c 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/CosmosUtils.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/CosmosUtils.java @@ -4,6 +4,7 @@ import com.azure.cosmos.CosmosClient; import com.azure.cosmos.CosmosClientBuilder; import com.google.common.annotations.VisibleForTesting; +import com.scalar.db.common.error.CoreError; import java.util.Locale; public final class CosmosUtils { @@ -35,7 +36,7 @@ static ConsistencyLevel getConsistencyLevel(CosmosConfig config) { if (consistencyLevel != ConsistencyLevel.STRONG && consistencyLevel != ConsistencyLevel.BOUNDED_STALENESS) { throw new IllegalArgumentException( - "The specified consistency level is not supported:" + consistencyLevel); + CoreError.INVALID_CONSISTENCY_LEVEL.buildMessage(consistencyLevel)); } return consistencyLevel; diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/MutateStatementHandler.java b/core/src/main/java/com/scalar/db/storage/cosmos/MutateStatementHandler.java index dfaf3b95ab..d0956687a1 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/MutateStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/MutateStatementHandler.java @@ -5,6 +5,7 @@ import com.scalar.db.api.Mutation; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -23,6 +24,7 @@ public abstract class MutateStatementHandler extends StatementHandler { public MutateStatementHandler(CosmosClient client, TableMetadataManager metadataManager) { super(client, metadataManager); } + /** * Executes the specified {@code Mutation} * @@ -35,7 +37,8 @@ public void handle(Mutation mutation) throws ExecutionException { } catch (CosmosException e) { throwException(e); } catch (RuntimeException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } @@ -61,11 +64,16 @@ private void throwException(CosmosException exception) throws ExecutionException int statusCode = exception.getSubStatusCode(); if (statusCode == CosmosErrorCode.PRECONDITION_FAILED.get()) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage(), exception); } else if (statusCode == CosmosErrorCode.RETRY_WITH.get()) { - throw new RetriableExecutionException(exception.getMessage(), exception); + throw new RetriableExecutionException( + CoreError.COSMOS_RETRY_WITH_ERROR_OCCURRED_IN_MUTATION.buildMessage( + exception.getMessage()), + exception); } - throw new ExecutionException(exception.getMessage(), exception); + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_MUTATION.buildMessage(exception.getMessage()), + exception); } } diff --git a/core/src/main/java/com/scalar/db/storage/cosmos/SelectStatementHandler.java b/core/src/main/java/com/scalar/db/storage/cosmos/SelectStatementHandler.java index f3fbee031e..22d9db2d59 100644 --- a/core/src/main/java/com/scalar/db/storage/cosmos/SelectStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/cosmos/SelectStatementHandler.java @@ -16,6 +16,7 @@ import com.scalar.db.api.TableMetadata; import com.scalar.db.common.EmptyScanner; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.Column; import com.scalar.db.util.ScalarDbUtils; @@ -63,14 +64,16 @@ protected Scanner handle(Selection selection) throws ExecutionException { } else { return executeQuery((Scan) selection, tableMetadata); } - } catch (CosmosException e) { if (e.getStatusCode() == CosmosErrorCode.NOT_FOUND.get()) { return new EmptyScanner(); } - throw new ExecutionException(e.getMessage(), e); + + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); } catch (RuntimeException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.COSMOS_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/BatchHandler.java b/core/src/main/java/com/scalar/db/storage/dynamo/BatchHandler.java index 81aed8e1d3..e8fc68a1c6 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/BatchHandler.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/BatchHandler.java @@ -7,6 +7,7 @@ import com.scalar.db.api.PutIfNotExists; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -66,7 +67,7 @@ public BatchHandler( */ public void handle(List mutations) throws ExecutionException { if (mutations.size() > 100) { - throw new IllegalArgumentException("DynamoDB cannot batch more than 100 mutations at once"); + throw new IllegalArgumentException(CoreError.DYNAMO_BATCH_SIZE_EXCEEDED.buildMessage()); } TableMetadata tableMetadata = metadataManager.getTableMetadata(mutations.get(0)); @@ -83,7 +84,7 @@ public void handle(List mutations) throws ExecutionException boolean allReasonsAreTransactionConflicts = true; for (CancellationReason reason : e.cancellationReasons()) { if (reason.code().equals("ConditionalCheckFailed")) { - throw new NoMutationException("No mutation was applied", e); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage(), e); } if (!reason.code().equals("TransactionConflict") && !reason.code().equals("None")) { allReasonsAreTransactionConflicts = false; @@ -92,11 +93,17 @@ public void handle(List mutations) throws ExecutionException if (allReasonsAreTransactionConflicts) { // If all the reasons of the cancellation are "TransactionConflict", throw // RetriableExecutionException - throw new RetriableExecutionException(e.getMessage(), e); + throw new RetriableExecutionException( + CoreError.DYNAMO_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION.buildMessage( + e.getMessage(), e), + e); } - throw new ExecutionException(e.getMessage(), e); + + throw new ExecutionException( + CoreError.DYNAMO_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } catch (DynamoDbException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.DYNAMO_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } @@ -186,8 +193,7 @@ private List copyAndAppendNamespacePrefix( } else if (m instanceof com.scalar.db.api.Delete) { return copyAndAppendNamespacePrefix((com.scalar.db.api.Delete) m); } else { - throw new IllegalArgumentException( - "Unexpected mutation type: " + m.getClass().getName()); + throw new AssertionError("Unexpected mutation type: " + m.getClass().getName()); } }) .collect(Collectors.toList()); diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/DeleteStatementHandler.java b/core/src/main/java/com/scalar/db/storage/dynamo/DeleteStatementHandler.java index a3b6f81d12..03806ffcfa 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/DeleteStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/DeleteStatementHandler.java @@ -6,6 +6,7 @@ import com.scalar.db.api.DeleteIfExists; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -48,11 +49,15 @@ public void handle(Delete delete) throws ExecutionException { try { delete(delete, tableMetadata); } catch (ConditionalCheckFailedException e) { - throw new NoMutationException("No mutation was applied", e); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage(), e); } catch (TransactionConflictException e) { - throw new RetriableExecutionException(e.getMessage(), e); + throw new RetriableExecutionException( + CoreError.DYNAMO_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION.buildMessage( + e.getMessage(), e), + e); } catch (DynamoDbException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.DYNAMO_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/Dynamo.java b/core/src/main/java/com/scalar/db/storage/dynamo/Dynamo.java index 3578bca0c2..b2243058b7 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/Dynamo.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/Dynamo.java @@ -15,6 +15,7 @@ import com.scalar.db.common.AbstractDistributedStorage; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import java.io.IOException; @@ -54,7 +55,8 @@ public Dynamo(DatabaseConfig databaseConfig) { if (databaseConfig.isCrossPartitionScanFilteringEnabled() || databaseConfig.isCrossPartitionScanOrderingEnabled()) { throw new IllegalArgumentException( - "Cross-partition scan with filtering or ordering is not supported in DynamoDB"); + CoreError.DYNAMO_CROSS_PARTITION_SCAN_WITH_FILTERING_OR_ORDERING_NOT_SUPPORTED + .buildMessage()); } DynamoConfig config = new DynamoConfig(databaseConfig); @@ -119,7 +121,8 @@ public Optional get(Get get) throws ExecutionException { scanner = selectStatementHandler.handle(get); Optional ret = scanner.one(); if (scanner.one().isPresent()) { - throw new IllegalArgumentException("Please use scan() for non-exact match selection"); + throw new IllegalArgumentException( + CoreError.GET_OPERATION_USED_FOR_NON_EXACT_MATCH_SELECTION.buildMessage(get)); } return ret; } finally { @@ -169,7 +172,7 @@ public void delete(List deletes) throws ExecutionException { @Override public void mutate(List mutations) throws ExecutionException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); if (mutations.size() == 1) { Mutation mutation = mutations.get(0); if (mutation instanceof Put) { diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/DynamoConfig.java b/core/src/main/java/com/scalar/db/storage/dynamo/DynamoConfig.java index 8f064d36d4..05964d3f78 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/DynamoConfig.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/DynamoConfig.java @@ -2,6 +2,7 @@ import static com.scalar.db.config.ConfigUtils.getString; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import java.util.Optional; import javax.annotation.Nullable; @@ -38,7 +39,7 @@ public DynamoConfig(DatabaseConfig databaseConfig) { } if (databaseConfig.getContactPoints().isEmpty()) { - throw new IllegalArgumentException(DatabaseConfig.CONTACT_POINTS + " is empty"); + throw new IllegalArgumentException(CoreError.INVALID_CONTACT_POINTS.buildMessage()); } region = databaseConfig.getContactPoints().get(0); accessKeyId = databaseConfig.getUsername().orElse(null); diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/DynamoOperationChecker.java b/core/src/main/java/com/scalar/db/storage/dynamo/DynamoOperationChecker.java index a7abd7d046..c0ee1859a4 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/DynamoOperationChecker.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/DynamoOperationChecker.java @@ -8,6 +8,7 @@ import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.ColumnChecker; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -30,8 +31,7 @@ public void check(Put put) throws ExecutionException { column -> { if (!new ColumnChecker(metadata, true, false, true, false).check(column)) { throw new IllegalArgumentException( - "A secondary index column cannot be set to null or an empty value (for Text and Blob) in DynamoDB. Operation: " - + put); + CoreError.DYNAMO_INDEX_COLUMN_CANNOT_BE_SET_TO_NULL_OR_EMPTY.buildMessage(put)); } }); checkCondition(put, metadata); @@ -55,7 +55,8 @@ private void checkCondition(Mutation mutation, TableMetadata metadata) { && expression.getOperator() != ConditionalExpression.Operator.IS_NULL && expression.getOperator() != ConditionalExpression.Operator.IS_NOT_NULL) { throw new IllegalArgumentException( - "DynamoDB only supports EQ, NE, IS_NULL, and IS_NOT_NULL operations for BOOLEAN type in conditions"); + CoreError.DYNAMO_CONDITION_OPERATION_NOT_SUPPORTED_FOR_BOOLEAN_TYPE.buildMessage( + mutation)); } } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/PutStatementHandler.java b/core/src/main/java/com/scalar/db/storage/dynamo/PutStatementHandler.java index f0f1d9472a..ed82f1dbc4 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/PutStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/PutStatementHandler.java @@ -7,6 +7,7 @@ import com.scalar.db.api.PutIfNotExists; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -49,11 +50,15 @@ public void handle(Put put) throws ExecutionException { try { execute(put, tableMetadata); } catch (ConditionalCheckFailedException e) { - throw new NoMutationException("No mutation was applied", e); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage(), e); } catch (TransactionConflictException e) { - throw new RetriableExecutionException(e.getMessage(), e); + throw new RetriableExecutionException( + CoreError.DYNAMO_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION.buildMessage( + e.getMessage(), e), + e); } catch (DynamoDbException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.DYNAMO_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/SelectStatementHandler.java b/core/src/main/java/com/scalar/db/storage/dynamo/SelectStatementHandler.java index 451f1e76be..ef7eb6fb1d 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/SelectStatementHandler.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/SelectStatementHandler.java @@ -14,6 +14,7 @@ import com.scalar.db.api.TableMetadata; import com.scalar.db.common.EmptyScanner; import com.scalar.db.common.TableMetadataManager; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.Column; import com.scalar.db.io.Key; @@ -89,9 +90,9 @@ public Scanner handle(Selection selection) throws ExecutionException { } else { return executeScan((Scan) selection, tableMetadata); } - } catch (DynamoDbException e) { - throw new ExecutionException(e.getMessage(), e); + throw new ExecutionException( + CoreError.DYNAMO_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/bytes/BlobBytesEncoder.java b/core/src/main/java/com/scalar/db/storage/dynamo/bytes/BlobBytesEncoder.java index 50c622b4ee..473c9d3830 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/bytes/BlobBytesEncoder.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/bytes/BlobBytesEncoder.java @@ -3,6 +3,7 @@ import static com.scalar.db.storage.dynamo.bytes.BytesUtils.mask; import com.scalar.db.api.Scan.Ordering.Order; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.BlobValue; import java.nio.ByteBuffer; import javax.annotation.concurrent.ThreadSafe; @@ -29,7 +30,8 @@ public void encode(BlobValue value, Order order, ByteBuffer dst) { for (byte b : value.getAsBytes().get()) { if (b == TERM) { throw new IllegalArgumentException( - "0x00 bytes not accepted in blob values in DESC order"); + CoreError.DYNAMO_ENCODER_0X00_BYTES_NOT_ACCEPTED_IN_BLOB_VALUES_IN_DESC_ORDER + .buildMessage()); } } } diff --git a/core/src/main/java/com/scalar/db/storage/dynamo/bytes/TextBytesEncoder.java b/core/src/main/java/com/scalar/db/storage/dynamo/bytes/TextBytesEncoder.java index 5dd4360759..a0e356cc89 100644 --- a/core/src/main/java/com/scalar/db/storage/dynamo/bytes/TextBytesEncoder.java +++ b/core/src/main/java/com/scalar/db/storage/dynamo/bytes/TextBytesEncoder.java @@ -3,6 +3,7 @@ import static com.scalar.db.storage.dynamo.bytes.BytesUtils.mask; import com.scalar.db.api.Scan.Ordering.Order; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.TextValue; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -24,7 +25,8 @@ public int encodedLength(TextValue value, Order order) { public void encode(TextValue value, Order order, ByteBuffer dst) { assert value.getAsString().isPresent(); if (value.getAsString().get().contains("\u0000")) { - throw new IllegalArgumentException("Can't encode a Text value containing '\\u0000'"); + throw new IllegalArgumentException( + CoreError.DYNAMO_ENCODER_CANNOT_ENCODE_TEXT_VALUE_CONTAINING_0X0000.buildMessage()); } byte[] bytes = value.getAsString().get().getBytes(StandardCharsets.UTF_8); diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java index de5dec5acc..7c1799750c 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java @@ -13,6 +13,7 @@ import com.scalar.db.api.Scan.Ordering; import com.scalar.db.api.Scan.Ordering.Order; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -81,7 +82,8 @@ public JdbcAdmin(BasicDataSource dataSource, JdbcConfig config) { public void createNamespace(String namespace, Map options) throws ExecutionException { if (!rdbEngine.isValidNamespaceOrTableName(namespace)) { - throw new ExecutionException("The schema name is not acceptable: " + namespace); + throw new IllegalArgumentException( + CoreError.JDBC_NAMESPACE_NAME_NOT_ACCEPTABLE.buildMessage(namespace)); } try (Connection connection = dataSource.getConnection()) { execute(connection, rdbEngine.createSchemaSqls(namespace)); @@ -113,9 +115,10 @@ void createTableInternal( String table, TableMetadata metadata, boolean ifNotExists) - throws ExecutionException, SQLException { + throws SQLException { if (!rdbEngine.isValidNamespaceOrTableName(table)) { - throw new ExecutionException("The table name is not acceptable: " + table); + throw new IllegalArgumentException( + CoreError.JDBC_TABLE_NAME_NOT_ACCEPTABLE.buildMessage(table)); } String createTableStatement = "CREATE TABLE " + encloseFullTableName(schema, table) + "("; // Order the columns for their creation by (partition keys >> clustering keys >> other columns) @@ -498,13 +501,13 @@ public TableMetadata getImportTableMetadata(String namespace, String table) if (!rdbEngine.isImportable()) { throw new UnsupportedOperationException( - "Importing table is not allowed in this storage: " + rdbEngine.getClass().getName()); + CoreError.JDBC_IMPORT_NOT_SUPPORTED.buildMessage(rdbEngine.getClass().getName())); } try (Connection connection = dataSource.getConnection()) { if (!tableExistsInternal(connection, namespace, table)) { throw new IllegalArgumentException( - "The " + getFullTableName(namespace, table) + " table does not exist"); + CoreError.TABLE_NOT_FOUND.buildMessage(getFullTableName(namespace, table))); } DatabaseMetaData metadata = connection.getMetaData(); @@ -517,7 +520,8 @@ public TableMetadata getImportTableMetadata(String namespace, String table) if (!primaryKeyExists) { throw new IllegalStateException( - "The " + getFullTableName(namespace, table) + " table must have a primary key"); + CoreError.JDBC_IMPORT_TABLE_WITHOUT_PRIMARY_KEY.buildMessage( + getFullTableName(namespace, table))); } resultSet = metadata.getColumns(null, namespace, table, "%"); @@ -762,7 +766,8 @@ private boolean tableExistsInternal(Connection connection, String namespace, Str public void repairNamespace(String namespace, Map options) throws ExecutionException { if (!rdbEngine.isValidNamespaceOrTableName(namespace)) { - throw new ExecutionException("The schema name is not acceptable: " + namespace); + throw new IllegalArgumentException( + CoreError.JDBC_NAMESPACE_NAME_NOT_ACCEPTABLE.buildMessage(namespace)); } try (Connection connection = dataSource.getConnection()) { createSchemaIfNotExists(connection, namespace); @@ -821,7 +826,7 @@ public void addRawColumnToTable( try (Connection connection = dataSource.getConnection()) { if (!tableExistsInternal(connection, namespace, table)) { throw new IllegalArgumentException( - "The " + getFullTableName(namespace, table) + " table does not exist"); + CoreError.TABLE_NOT_FOUND.buildMessage(getFullTableName(namespace, table))); } String addNewColumnStatement = diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcConfig.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcConfig.java index 96bb8ac084..e3d458aa42 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcConfig.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcConfig.java @@ -4,6 +4,7 @@ import static com.scalar.db.config.ConfigUtils.getInt; import static com.scalar.db.config.ConfigUtils.getString; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import java.util.Locale; import java.util.Optional; @@ -95,7 +96,7 @@ public JdbcConfig(DatabaseConfig databaseConfig) { } if (databaseConfig.getContactPoints().isEmpty()) { - throw new IllegalArgumentException(DatabaseConfig.CONTACT_POINTS + " is empty"); + throw new IllegalArgumentException(CoreError.INVALID_CONTACT_POINTS.buildMessage()); } jdbcUrl = databaseConfig.getContactPoints().get(0); username = databaseConfig.getUsername().orElse(null); diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java index 022c9786c9..cf9077023a 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcDatabase.java @@ -13,6 +13,7 @@ import com.scalar.db.common.AbstractDistributedStorage; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; @@ -85,7 +86,8 @@ public Optional get(Get get) throws ExecutionException { connection = dataSource.getConnection(); return jdbcService.get(get, connection); } catch (SQLException e) { - throw new ExecutionException("Get operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); } finally { close(connection); } @@ -100,7 +102,8 @@ public Scanner scan(Scan scan) throws ExecutionException { return jdbcService.getScanner(scan, connection); } catch (SQLException e) { close(connection); - throw new ExecutionException("Scan operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_SELECTION.buildMessage(e.getMessage()), e); } } @@ -111,10 +114,11 @@ public void put(Put put) throws ExecutionException { try { connection = dataSource.getConnection(); if (!jdbcService.put(put, connection)) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage()); } } catch (SQLException e) { - throw new ExecutionException("Put operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } finally { close(connection); } @@ -132,10 +136,11 @@ public void delete(Delete delete) throws ExecutionException { try { connection = dataSource.getConnection(); if (!jdbcService.delete(delete, connection)) { - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage()); } } catch (SQLException e) { - throw new ExecutionException("Delete operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } finally { close(connection); } @@ -165,7 +170,8 @@ public void mutate(List mutations) throws ExecutionException connection.setAutoCommit(false); } catch (SQLException e) { close(connection); - throw new ExecutionException("Mutate operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } try { @@ -173,9 +179,10 @@ public void mutate(List mutations) throws ExecutionException try { connection.rollback(); } catch (SQLException e) { - throw new ExecutionException("Failed to rollback", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } - throw new NoMutationException("No mutation was applied"); + throw new NoMutationException(CoreError.NO_MUTATION_APPLIED.buildMessage()); } else { connection.commit(); } @@ -183,14 +190,18 @@ public void mutate(List mutations) throws ExecutionException try { connection.rollback(); } catch (SQLException sqlException) { - throw new ExecutionException("Failed to rollback", sqlException); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } - if (rdbEngine.isConflictError(e)) { + if (rdbEngine.isConflict(e)) { // Since a mutate operation executes multiple put/delete operations in a transaction, - // conflicts can happen. Throw RetriableExecutionException in that case. - throw new RetriableExecutionException("Conflict happened in a mutate operation", e); + // conflicts can occur. Throw RetriableExecutionException in that case. + throw new RetriableExecutionException( + CoreError.JDBC_TRANSACTION_CONFLICT_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), + e); } - throw new ExecutionException("Mutate operation failed", e); + throw new ExecutionException( + CoreError.JDBC_ERROR_OCCURRED_IN_MUTATION.buildMessage(e.getMessage()), e); } finally { close(connection); } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcService.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcService.java index 28db40dd8c..f44388583d 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcService.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcService.java @@ -13,6 +13,7 @@ import com.scalar.db.api.TableMetadata; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.storage.jdbc.query.DeleteQuery; import com.scalar.db.storage.jdbc.query.QueryBuilder; @@ -72,7 +73,8 @@ public Optional get(Get get, Connection connection) Optional.of( new ResultInterpreter(get.getProjections(), tableMetadata).interpret(resultSet)); if (resultSet.next()) { - throw new IllegalArgumentException("Please use scan() for non-exact match selection"); + throw new IllegalArgumentException( + CoreError.GET_OPERATION_USED_FOR_NON_EXACT_MATCH_SELECTION.buildMessage(get)); } return ret; } @@ -202,7 +204,7 @@ private boolean deleteInternal(Delete delete, Connection connection) public boolean mutate(List mutations, Connection connection) throws SQLException, ExecutionException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); operationChecker.check(mutations); for (Mutation mutation : mutations) { diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineFactory.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineFactory.java index 63bc9d9383..401d65a913 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineFactory.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineFactory.java @@ -1,5 +1,6 @@ package com.scalar.db.storage.jdbc; +import com.scalar.db.common.error.CoreError; import java.sql.Connection; import java.sql.SQLException; @@ -30,7 +31,8 @@ static RdbEngineStrategy create(String jdbcUrl) { } else if (jdbcUrl.startsWith("jdbc:sqlite:")) { return new RdbEngineSqlite(); } else { - throw new IllegalArgumentException("The rdb engine is not supported: " + jdbcUrl); + throw new IllegalArgumentException( + CoreError.JDBC_RDB_ENGINE_NOT_SUPPORTED.buildMessage(jdbcUrl)); } } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java index e692ed7fb8..b87e99616a 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java @@ -2,6 +2,7 @@ import com.scalar.db.api.LikeExpression; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; import com.scalar.db.storage.jdbc.query.InsertOnDuplicateKeyUpdateQuery; @@ -159,7 +160,7 @@ public boolean isUndefinedTableError(SQLException e) { } @Override - public boolean isConflictError(SQLException e) { + public boolean isConflict(SQLException e) { // Error number: 1213; Symbol: ER_LOCK_DEADLOCK; SQLSTATE: 40001 // Message: Deadlock found when trying to get lock; try restarting transaction @@ -194,8 +195,7 @@ public String getDataTypeForEngine(DataType scalarDbDataType) { case TEXT: return "LONGTEXT"; default: - assert false; - return null; + throw new AssertionError(); } } @@ -218,8 +218,8 @@ public DataType getDataTypeForScalarDb( case BIT: if (columnSize != 1) { throw new IllegalArgumentException( - String.format( - "Data type %s(%d) is unsupported: %s", typeName, columnSize, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_WITH_SIZE_NOT_SUPPORTED.buildMessage( + typeName, columnSize, columnDescription)); } return DataType.BOOLEAN; case TINYINT: @@ -241,7 +241,8 @@ public DataType getDataTypeForScalarDb( case BIGINT: if (typeName.toUpperCase().endsWith("UNSIGNED")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } logger.warn( "Data type that may be smaller than that of underlying database is assigned: {} (MySQL {} to ScalarDB BIGINT)", @@ -258,7 +259,8 @@ public DataType getDataTypeForScalarDb( if (!typeName.toUpperCase().endsWith("CHAR") && !typeName.toUpperCase().endsWith("TEXT")) { // to exclude ENUM, SET, JSON, etc. throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } if (!typeName.equalsIgnoreCase("LONGTEXT")) { logger.info( @@ -273,7 +275,8 @@ public DataType getDataTypeForScalarDb( if (!typeName.toUpperCase().endsWith("BINARY") && !typeName.toUpperCase().endsWith("BLOB")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } if (!typeName.equalsIgnoreCase("LONGBLOB")) { logger.info( @@ -284,7 +287,8 @@ public DataType getDataTypeForScalarDb( return DataType.BLOB; default: throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java index af3d17a50e..8d43604cc9 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java @@ -4,6 +4,7 @@ import com.scalar.db.api.LikeExpression; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; import com.scalar.db.storage.jdbc.query.MergeIntoQuery; @@ -165,7 +166,7 @@ public boolean isUndefinedTableError(SQLException e) { } @Override - public boolean isConflictError(SQLException e) { + public boolean isConflict(SQLException e) { // ORA-08177: can't serialize access for this transaction // ORA-00060: deadlock detected while waiting for resource return e.getErrorCode() == 8177 || e.getErrorCode() == 60; @@ -197,8 +198,7 @@ public String getDataTypeForEngine(DataType scalarDbDataType) { case TEXT: return "VARCHAR2(4000)"; default: - assert false; - return null; + throw new AssertionError(); } } @@ -222,8 +222,8 @@ public DataType getDataTypeForScalarDb( case NUMERIC: if (columnSize > 15) { throw new IllegalArgumentException( - String.format( - "Data type %s is unsupported: %s", numericTypeDescription, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + numericTypeDescription, columnDescription)); } if (digits == 0) { logger.info( @@ -242,8 +242,8 @@ public DataType getDataTypeForScalarDb( case FLOAT: if (columnSize > 53) { throw new IllegalArgumentException( - String.format( - "Data type %s is unsupported: %s", numericTypeDescription, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + numericTypeDescription, columnDescription)); } if (columnSize < 53) { logger.info( @@ -282,7 +282,8 @@ public DataType getDataTypeForScalarDb( return DataType.BLOB; default: throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java index e445c13ac8..ee38705a24 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java @@ -3,6 +3,7 @@ import static com.scalar.db.util.ScalarDbUtils.getFullTableName; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; import com.scalar.db.storage.jdbc.query.InsertOnConflictDoUpdateQuery; @@ -147,7 +148,7 @@ public boolean isUndefinedTableError(SQLException e) { } @Override - public boolean isConflictError(SQLException e) { + public boolean isConflict(SQLException e) { if (e.getSQLState() == null) { return false; } @@ -195,8 +196,7 @@ public String getDataTypeForEngine(DataType scalarDbDataType) { case TEXT: return "TEXT"; default: - assert false; - return null; + throw new AssertionError(); } } @@ -215,15 +215,15 @@ public DataType getDataTypeForScalarDb( case BIT: if (columnSize != 1) { throw new IllegalArgumentException( - String.format( - "Data type %s(%d) is unsupported: %s", typeName, columnSize, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_WITH_SIZE_NOT_SUPPORTED.buildMessage( + typeName, columnSize, columnDescription)); } return DataType.BOOLEAN; case SMALLINT: if (typeName.equalsIgnoreCase("smallserial")) { throw new IllegalArgumentException( - String.format( - "Data type %s(%d) is unsupported: %s", typeName, columnSize, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } logger.info( "Data type larger than that of underlying database is assigned: {} ({} to INT)", @@ -233,14 +233,15 @@ public DataType getDataTypeForScalarDb( case INTEGER: if (typeName.equalsIgnoreCase("serial")) { throw new IllegalArgumentException( - String.format( - "Data type %s(%d) is unsupported: %s", typeName, columnSize, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } return DataType.INT; case BIGINT: if (typeName.equalsIgnoreCase("bigserial")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } logger.warn( "Data type that may be smaller than that of underlying database is assigned: {} (PostgreSQL {} to ScalarDB BIGINT)", @@ -252,7 +253,8 @@ public DataType getDataTypeForScalarDb( case DOUBLE: if (!typeName.equalsIgnoreCase("float8")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } return DataType.DOUBLE; case CHAR: @@ -268,7 +270,8 @@ public DataType getDataTypeForScalarDb( return DataType.BLOB; default: throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java index 6696aa970d..c75b0f9d8e 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java @@ -2,6 +2,7 @@ import com.scalar.db.api.LikeExpression; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; import com.scalar.db.storage.jdbc.query.MergeQuery; @@ -129,7 +130,7 @@ public boolean isUndefinedTableError(SQLException e) { } @Override - public boolean isConflictError(SQLException e) { + public boolean isConflict(SQLException e) { // 1205: Transaction (Process ID %d) was deadlocked on %.*ls resources with another process and // has been chosen as the deadlock victim. Rerun the transaction. return e.getErrorCode() == 1205; @@ -177,8 +178,7 @@ public String getDataTypeForEngine(DataType scalarDbDataType) { case TEXT: return "VARCHAR(8000) COLLATE Latin1_General_BIN"; default: - assert false; - return null; + throw new AssertionError(); } } @@ -195,8 +195,8 @@ public DataType getDataTypeForScalarDb( case BIT: if (columnSize != 1) { throw new IllegalArgumentException( - String.format( - "Data type %s(%d) is unsupported: %s", typeName, columnSize, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_WITH_SIZE_NOT_SUPPORTED.buildMessage( + typeName, columnSize, columnDescription)); } return DataType.BOOLEAN; case TINYINT: @@ -224,7 +224,8 @@ public DataType getDataTypeForScalarDb( case NVARCHAR: if (typeName.equalsIgnoreCase("uniqueidentifier")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } logger.info( "Data type larger than that of underlying database is assigned: {} ({} to TEXT)", @@ -235,14 +236,16 @@ public DataType getDataTypeForScalarDb( case LONGNVARCHAR: if (typeName.equalsIgnoreCase("xml")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } return DataType.TEXT; case BINARY: case VARBINARY: if (!typeName.equalsIgnoreCase("binary") && !typeName.equalsIgnoreCase("varbinary")) { throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } if (columnSize < Integer.MAX_VALUE) { logger.info( @@ -255,7 +258,8 @@ public DataType getDataTypeForScalarDb( return DataType.BLOB; default: throw new IllegalArgumentException( - String.format("Data type %s is unsupported: %s", typeName, columnDescription)); + CoreError.JDBC_IMPORT_DATA_TYPE_NOT_SUPPORTED.buildMessage( + typeName, columnDescription)); } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java index bf28a36077..20c92c86a2 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java @@ -62,7 +62,7 @@ public boolean isUndefinedTableError(SQLException e) { } @Override - public boolean isConflictError(SQLException e) { + public boolean isConflict(SQLException e) { // Error code: SQLITE_BUSY (5) // Message: The database file is locked (database is locked) @@ -135,7 +135,7 @@ public String getTextType(int charLength) { @Override public DataType getDataTypeForScalarDb( JDBCType type, String typeName, int columnSize, int digits, String columnDescription) { - throw new UnsupportedOperationException("SQLite is not supported"); + throw new AssertionError("SQLite is not supported"); } @Override diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java index b71ac67008..0db51ebbc7 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java @@ -23,7 +23,7 @@ public interface RdbEngineStrategy { boolean isUndefinedTableError(SQLException e); - boolean isConflictError(SQLException e); + boolean isConflict(SQLException e); String getDataTypeForEngine(DataType dataType); diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/ScannerImpl.java b/core/src/main/java/com/scalar/db/storage/jdbc/ScannerImpl.java index 66e0101130..813ece2c46 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/ScannerImpl.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/ScannerImpl.java @@ -3,6 +3,7 @@ import com.scalar.db.api.Result; import com.scalar.db.api.Scanner; import com.scalar.db.common.ScannerIterator; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.sql.Connection; @@ -50,7 +51,7 @@ public Optional one() throws ExecutionException { } return Optional.empty(); } catch (SQLException e) { - throw new ExecutionException("Failed to fetch the next result", e); + throw new ExecutionException(CoreError.JDBC_FETCHING_NEXT_RESULT_FAILED.buildMessage(), e); } } @@ -63,7 +64,7 @@ public List all() throws ExecutionException { } return ret; } catch (SQLException e) { - throw new ExecutionException("Failed to fetch the next result", e); + throw new ExecutionException(CoreError.JDBC_FETCHING_NEXT_RESULT_FAILED.buildMessage(), e); } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/query/SimpleSelectQuery.java b/core/src/main/java/com/scalar/db/storage/jdbc/query/SimpleSelectQuery.java index f7caf677b2..ab80c39354 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/query/SimpleSelectQuery.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/query/SimpleSelectQuery.java @@ -148,7 +148,7 @@ private String convert(ConditionalExpression condition) { case NOT_LIKE: return convert((LikeExpression) condition); default: - throw new IllegalArgumentException("Unknown operator: " + condition.getOperator()); + throw new AssertionError("Unknown operator: " + condition.getOperator()); } } diff --git a/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorage.java b/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorage.java index 433e3aaafa..71b7618852 100644 --- a/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorage.java +++ b/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorage.java @@ -14,6 +14,7 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.Scanner; import com.scalar.db.common.AbstractDistributedStorage; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.service.StorageFactory; @@ -122,7 +123,7 @@ public void delete(List deletes) throws ExecutionException { @Override public void mutate(List mutations) throws ExecutionException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); if (mutations.size() == 1) { Mutation mutation = mutations.get(0); if (mutation instanceof Put) { diff --git a/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorageConfig.java b/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorageConfig.java index 8ad593c690..3b7bd40f28 100644 --- a/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorageConfig.java +++ b/core/src/main/java/com/scalar/db/storage/multistorage/MultiStorageConfig.java @@ -4,6 +4,7 @@ import static com.scalar.db.config.ConfigUtils.getStringArray; import com.google.common.collect.ImmutableMap; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import java.util.Map; import java.util.Properties; @@ -80,7 +81,8 @@ private ImmutableMap loadDatabasePropertiesMapping(Propertie if (dbProps.getProperty(DatabaseConfig.STORAGE).equals(STORAGE_NAME)) { throw new IllegalArgumentException( - "Does not support nested " + STORAGE_NAME + ": " + storage); + CoreError.MULTI_STORAGE_NESTED_MULTI_STORAGE_DEFINITION_NOT_SUPPORTED.buildMessage( + storage)); } builder.put(storage, dbProps); @@ -133,7 +135,8 @@ private ImmutableMap loadNamespaceStorageMapping(Properties prop private void checkIfStorageExists(String storage) { if (storage == null || !databasePropertiesMap.containsKey(storage)) { - throw new IllegalArgumentException("Storage not found: " + storage); + throw new IllegalArgumentException( + CoreError.MULTI_STORAGE_STORAGE_NOT_FOUND.buildMessage(storage)); } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CommitHandler.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CommitHandler.java index 902882ccdb..e51048275b 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CommitHandler.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CommitHandler.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableList; import com.scalar.db.api.DistributedStorage; import com.scalar.db.api.TransactionState; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.storage.NoMutationException; import com.scalar.db.exception.storage.RetriableExecutionException; @@ -75,12 +76,16 @@ public void prepare(Snapshot snapshot) throws PreparationException { try { prepareRecords(snapshot); } catch (NoMutationException e) { - throw new PreparationConflictException("Preparing record exists", e, snapshot.getId()); + throw new PreparationConflictException( + CoreError.CONSENSUS_COMMIT_PREPARING_RECORD_EXISTS.buildMessage(), e, snapshot.getId()); } catch (RetriableExecutionException e) { throw new PreparationConflictException( - "Conflict happened when preparing records", e, snapshot.getId()); + CoreError.CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHEN_PREPARING_RECORDS.buildMessage(), + e, + snapshot.getId()); } catch (ExecutionException e) { - throw new PreparationException("Preparing records failed", e, snapshot.getId()); + throw new PreparationException( + CoreError.CONSENSUS_COMMIT_PREPARING_RECORDS_FAILED.buildMessage(), e, snapshot.getId()); } } @@ -104,12 +109,13 @@ public void validate(Snapshot snapshot) throws ValidationException { // validation is executed when SERIALIZABLE with EXTRA_READ strategy is chosen. snapshot.toSerializableWithExtraRead(storage); } catch (ExecutionException e) { - throw new ValidationException("Validation failed", e, snapshot.getId()); + throw new ValidationException( + CoreError.CONSENSUS_COMMIT_VALIDATION_FAILED.buildMessage(), e, snapshot.getId()); } } public void commitState(Snapshot snapshot) - throws CommitException, UnknownTransactionStatusException { + throws CommitConflictException, UnknownTransactionStatusException { String id = snapshot.getId(); try { Coordinator.State state = new Coordinator.State(id, TransactionState.COMMITTED); @@ -123,20 +129,26 @@ public void commitState(Snapshot snapshot) TransactionState state = s.get().getState(); if (state.equals(TransactionState.ABORTED)) { rollbackRecords(snapshot); - throw new CommitException( - "Committing state in coordinator failed. the transaction is aborted", e, id); + throw new CommitConflictException( + CoreError.CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHEN_COMMITTING_STATE.buildMessage(), + e, + id); } } else { throw new UnknownTransactionStatusException( - "Committing state failed with NoMutationException but the coordinator status doesn't exist", + CoreError + .CONSENSUS_COMMIT_COMMITTING_STATE_FAILED_WITH_NO_MUTATION_EXCEPTION_BUT_COORDINATOR_STATUS_DOES_NOT_EXIST + .buildMessage(), e, id); } } catch (CoordinatorException e1) { - throw new UnknownTransactionStatusException("Can't get the state", e1, id); + throw new UnknownTransactionStatusException( + CoreError.CONSENSUS_COMMIT_CANNOT_GET_STATE.buildMessage(), e1, id); } } catch (CoordinatorException e) { - throw new UnknownTransactionStatusException("Coordinator status is unknown", e, id); + throw new UnknownTransactionStatusException( + CoreError.CONSENSUS_COMMIT_UNKNOWN_COORDINATOR_STATUS.buildMessage(), e, id); } } @@ -171,14 +183,18 @@ public TransactionState abortState(String id) throws UnknownTransactionStatusExc return state.get().getState(); } throw new UnknownTransactionStatusException( - "Aborting state failed with NoMutationException but the coordinator status doesn't exist", + CoreError + .CONSENSUS_COMMIT_ABORTING_STATE_FAILED_WITH_NO_MUTATION_EXCEPTION_BUT_COORDINATOR_STATUS_DOES_NOT_EXIST + .buildMessage(), e, id); } catch (CoordinatorException e1) { - throw new UnknownTransactionStatusException("Can't get the state", e1, id); + throw new UnknownTransactionStatusException( + CoreError.CONSENSUS_COMMIT_CANNOT_GET_STATE.buildMessage(), e1, id); } } catch (CoordinatorException e) { - throw new UnknownTransactionStatusException("Coordinator status is unknown", e, id); + throw new UnknownTransactionStatusException( + CoreError.CONSENSUS_COMMIT_UNKNOWN_COORDINATOR_STATUS.buildMessage(), e, id); } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommit.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommit.java index 0ae76d0eb0..2b66a5179c 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommit.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommit.java @@ -11,6 +11,7 @@ import com.scalar.db.api.Result; import com.scalar.db.api.Scan; import com.scalar.db.common.AbstractDistributedTransaction; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.transaction.CommitConflictException; import com.scalar.db.exception.transaction.CommitException; @@ -137,9 +138,13 @@ public void commit() throws CommitException, UnknownTransactionStatusException { if (e instanceof UncommittedRecordException) { lazyRecovery((UncommittedRecordException) e); } - throw new CommitConflictException("Conflict occurred while implicit pre-read", e, getId()); + throw new CommitConflictException( + CoreError.CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHILE_IMPLICIT_PRE_READ.buildMessage(), + e, + getId()); } catch (CrudException e) { - throw new CommitException("Failed to execute implicit pre-read", e, getId()); + throw new CommitException( + CoreError.CONSENSUS_COMMIT_EXECUTING_IMPLICIT_PRE_READ_FAILED.buildMessage(), e, getId()); } commit.commit(crud.getSnapshot()); @@ -180,7 +185,7 @@ private void checkMutation(Mutation mutation) throws CrudException { try { mutationOperationChecker.check(mutation); } catch (ExecutionException e) { - throw new CrudException("Checking the operation failed", e, getId()); + throw new CrudException(e.getMessage(), e, getId()); } } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitAdmin.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitAdmin.java index d7b2259469..f325960f15 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitAdmin.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitAdmin.java @@ -10,6 +10,7 @@ import com.scalar.db.api.DistributedStorageAdmin; import com.scalar.db.api.DistributedTransactionAdmin; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; @@ -61,7 +62,8 @@ public ConsensusCommitAdmin(DatabaseConfig databaseConfig) { @Override public void createCoordinatorTables(Map options) throws ExecutionException { if (coordinatorTablesExist()) { - throw new IllegalArgumentException("Coordinator tables already exist"); + throw new IllegalArgumentException( + CoreError.CONSENSUS_COMMIT_COORDINATOR_TABLES_ALREADY_EXIST.buildMessage()); } admin.createNamespace(coordinatorNamespace, options); @@ -71,7 +73,8 @@ public void createCoordinatorTables(Map options) throws Executio @Override public void dropCoordinatorTables() throws ExecutionException { if (!coordinatorTablesExist()) { - throw new IllegalArgumentException("Coordinator tables do not exist"); + throw new IllegalArgumentException( + CoreError.CONSENSUS_COMMIT_COORDINATOR_TABLES_NOT_FOUND.buildMessage()); } admin.dropTable(coordinatorNamespace, Coordinator.TABLE); @@ -81,7 +84,8 @@ public void dropCoordinatorTables() throws ExecutionException { @Override public void truncateCoordinatorTables() throws ExecutionException { if (!coordinatorTablesExist()) { - throw new IllegalArgumentException("Coordinator tables do not exist"); + throw new IllegalArgumentException( + CoreError.CONSENSUS_COMMIT_COORDINATOR_TABLES_NOT_FOUND.buildMessage()); } admin.truncateTable(coordinatorNamespace, Coordinator.TABLE); @@ -224,7 +228,7 @@ public void addNewColumnToTable( TableMetadata tableMetadata = getTableMetadata(namespace, table); if (tableMetadata == null) { throw new IllegalArgumentException( - "Table does not exist: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage(ScalarDbUtils.getFullTableName(namespace, table))); } String beforeColumnName = getBeforeImageColumnName(columnName, tableMetadata); @@ -247,7 +251,8 @@ public void importTable(String namespace, String table, Map opti TableMetadata tableMetadata = admin.getTableMetadata(namespace, table); if (tableMetadata != null) { throw new IllegalArgumentException( - "Table already exists: " + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_ALREADY_EXISTS.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table))); } tableMetadata = admin.getImportTableMetadata(namespace, table); @@ -283,9 +288,7 @@ public void close() { private void checkNamespace(String namespace) { if (namespace.equals(coordinatorNamespace)) { throw new IllegalArgumentException( - "Namespace " - + namespace - + " is reserved. Any operations on this namespace are not allowed."); + CoreError.CONSENSUS_COMMIT_COORDINATOR_NAMESPACE_SPECIFIED.buildMessage(namespace)); } } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationChecker.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationChecker.java index b281072bc0..e4a9f30794 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationChecker.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationChecker.java @@ -14,6 +14,7 @@ import com.scalar.db.api.PutIfNotExists; import com.scalar.db.api.TableMetadata; import com.scalar.db.common.checker.ConditionChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import javax.annotation.concurrent.ThreadSafe; @@ -32,7 +33,7 @@ private TransactionTableMetadata getTableMetadata(Operation operation) throws Ex transactionTableMetadataManager.getTransactionTableMetadata(operation); if (metadata == null) { throw new IllegalArgumentException( - "The specified table is not found: " + operation.forFullTableName().get()); + CoreError.TABLE_NOT_FOUND.buildMessage(operation.forFullTableName().get())); } return metadata; } @@ -57,7 +58,8 @@ private void check(Put put) throws ExecutionException { for (String column : put.getContainedColumnNames()) { if (metadata.getTransactionMetaColumnNames().contains(column)) { throw new IllegalArgumentException( - "Mutating transaction metadata columns is not allowed. column: " + column); + CoreError.CONSENSUS_COMMIT_MUTATING_TRANSACTION_METADATA_COLUMNS_NOT_ALLOWED + .buildMessage(put.forFullTableName().get(), column)); } } @@ -70,9 +72,8 @@ private void check(Put put) throws ExecutionException { || condition instanceof PutIfNotExists || condition instanceof PutIfExists)) { throw new IllegalArgumentException( - "A " - + condition.getClass().getSimpleName() - + " condition is not allowed on Put operation"); + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_ON_PUT.buildMessage( + condition.getClass().getSimpleName())); } checkConditionIsNotTargetingMetadataColumns(condition, metadata); ConditionChecker conditionChecker = createConditionChecker(metadata.getTableMetadata()); @@ -87,9 +88,8 @@ private void check(Delete delete) throws ExecutionException { if (!(condition instanceof DeleteIf || condition instanceof DeleteIfExists)) { throw new IllegalArgumentException( - "A " - + condition.getClass().getSimpleName() - + " condition is not allowed on Delete operation"); + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_ON_DELETE.buildMessage( + condition.getClass().getSimpleName())); } TransactionTableMetadata transactionMetadata = getTableMetadata(delete); checkConditionIsNotTargetingMetadataColumns(condition, transactionMetadata); @@ -104,8 +104,8 @@ private void checkConditionIsNotTargetingMetadataColumns( String column = expression.getColumn().getName(); if (metadata.getTransactionMetaColumnNames().contains(column)) { throw new IllegalArgumentException( - "The condition is not allowed to target transaction metadata columns. column: " - + column); + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_ALLOWED_TO_TARGET_TRANSACTION_METADATA_COLUMNS + .buildMessage(column)); } } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java index ce4e946ea3..3b3b027ce0 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitUtils.java @@ -2,6 +2,7 @@ import com.google.common.collect.ImmutableMap; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.DataType; import java.util.Collections; import java.util.HashSet; @@ -63,7 +64,8 @@ private static void checkIsNotTransactionMetaColumn(Set columnNames) { c -> { if (columnNames.contains(c)) { throw new IllegalArgumentException( - "Column \"" + c + "\" is reserved as transaction metadata"); + CoreError.CONSENSUS_COMMIT_COLUMN_RESERVED_AS_TRANSACTION_METADATA.buildMessage( + c)); } }); } @@ -75,11 +77,9 @@ private static void checkBeforeColumnsDoNotAlreadyExist( String beforePrefixed = Attribute.BEFORE_PREFIX + c; if (tableMetadata.getColumnNames().contains(beforePrefixed)) { throw new IllegalArgumentException( - "Non-primary key column with the \"" - + Attribute.BEFORE_PREFIX - + "\" prefix, \"" - + beforePrefixed - + "\", is reserved as transaction metadata"); + CoreError + .CONSENSUS_COMMIT_BEFORE_PREFIXED_COLUMN_FOR_NON_PRIMARY_KEY_RESERVED_AS_TRANSACTION_METADATA + .buildMessage(beforePrefixed)); } }); } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java index 5fb651868c..623823e92c 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/CrudHandler.java @@ -13,6 +13,7 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.Scanner; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.transaction.CrudException; import com.scalar.db.util.ScalarDbUtils; @@ -89,7 +90,10 @@ private void read(Snapshot.Key key, Get get) throws CrudException { return; } throw new UncommittedRecordException( - get, result.get(), "This record needs recovery", snapshot.getId()); + get, + result.get(), + CoreError.CONSENSUS_COMMIT_READ_UNCOMMITTED_RECORD.buildMessage(), + snapshot.getId()); } private Optional createGetResult(Snapshot.Key key, List projections) @@ -136,7 +140,10 @@ private List scanInternal(Scan scan) throws CrudException { TransactionResult result = new TransactionResult(r); if (!result.isCommitted()) { throw new UncommittedRecordException( - scan, result, "The record needs recovery", snapshot.getId()); + scan, + result, + CoreError.CONSENSUS_COMMIT_READ_UNCOMMITTED_RECORD.buildMessage(), + snapshot.getId()); } Snapshot.Key key = new Snapshot.Key(scan, r); @@ -176,9 +183,9 @@ public void put(Put put) throws CrudException { if (put.getCondition().isPresent() && (!put.isImplicitPreReadEnabled() && !snapshot.containsKeyInReadSet(key))) { throw new IllegalArgumentException( - "Put cannot have a condition when the target record is unread and implicit pre-read is disabled." - + " Please read the target record beforehand or enable implicit pre-read: " - + put); + CoreError + .CONSENSUS_COMMIT_PUT_CANNOT_HAVE_CONDITION_WHEN_TARGET_RECORD_UNREAD_AND_IMPLICIT_PRE_READ_DISABLED + .buildMessage(put)); } if (put.getCondition().isPresent()) { @@ -252,7 +259,8 @@ Optional getFromStorage(Get get) throws CrudException { get.withConsistency(Consistency.LINEARIZABLE); return storage.get(get).map(TransactionResult::new); } catch (ExecutionException e) { - throw new CrudException("Get failed", e, snapshot.getId()); + throw new CrudException( + CoreError.CONSENSUS_COMMIT_GET_OPERATION_FAILED.buildMessage(), e, snapshot.getId()); } } @@ -269,7 +277,8 @@ private Scanner getFromStorage(Scan scan) throws CrudException { scan.withConsistency(Consistency.LINEARIZABLE); return storage.scan(scan); } catch (ExecutionException e) { - throw new CrudException("Scan failed", e, snapshot.getId()); + throw new CrudException( + CoreError.CONSENSUS_COMMIT_SCAN_OPERATION_FAILED.buildMessage(), e, snapshot.getId()); } } @@ -279,12 +288,12 @@ private TableMetadata getTableMetadata(String namespace, String table) throws Cr tableMetadataManager.getTransactionTableMetadata(namespace, table); if (metadata == null) { throw new IllegalArgumentException( - "The specified table is not found: " - + ScalarDbUtils.getFullTableName(namespace, table)); + CoreError.TABLE_NOT_FOUND.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table))); } return metadata.getTableMetadata(); } catch (ExecutionException e) { - throw new CrudException("Getting a table metadata failed", e, snapshot.getId()); + throw new CrudException(e.getMessage(), e, snapshot.getId()); } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/MutationConditionsValidator.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/MutationConditionsValidator.java index f42e4d380d..1423d72b45 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/MutationConditionsValidator.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/MutationConditionsValidator.java @@ -11,6 +11,7 @@ import com.scalar.db.api.PutIf; import com.scalar.db.api.PutIfExists; import com.scalar.db.api.PutIfNotExists; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.transaction.UnsatisfiedConditionException; import com.scalar.db.io.Column; import java.util.List; @@ -92,18 +93,16 @@ public void checkIfConditionIsSatisfied(Delete delete, @Nullable TransactionResu private void throwWhenRecordDoesNotExist(MutationCondition condition) throws UnsatisfiedConditionException { throw new UnsatisfiedConditionException( - "The record does not exist so the " - + condition.getClass().getSimpleName() - + " condition is not satisfied", + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED_BECAUSE_RECORD_NOT_EXISTS.buildMessage( + condition.getClass().getSimpleName()), transactionId); } private void throwWhenRecordExists(MutationCondition condition) throws UnsatisfiedConditionException { throw new UnsatisfiedConditionException( - "The record exists so the " - + condition.getClass().getSimpleName() - + " condition is not satisfied", + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED_BECAUSE_RECORD_EXISTS.buildMessage( + condition.getClass().getSimpleName()), transactionId); } @@ -116,9 +115,8 @@ private void validateConditionalExpressions( conditionalExpression.getColumn(), conditionalExpression.getOperator())) { throw new UnsatisfiedConditionException( - "The condition on the column '" - + conditionalExpression.getColumn().getName() - + "' is not satisfied", + CoreError.CONSENSUS_COMMIT_CONDITION_NOT_SATISFIED.buildMessage( + conditionalExpression.getColumn().getName()), transactionId); } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java index 1e0db045e9..eb52ed8414 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/Snapshot.java @@ -18,6 +18,7 @@ import com.scalar.db.api.ScanAll; import com.scalar.db.api.Scanner; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.transaction.CrudException; import com.scalar.db.exception.transaction.PreparationConflictException; @@ -121,7 +122,8 @@ public void put(Scan scan, List keys) { public void put(Key key, Put put) { if (deleteSet.containsKey(key)) { - throw new IllegalArgumentException("Writing already deleted data is not allowed"); + throw new IllegalArgumentException( + CoreError.CONSENSUS_COMMIT_WRITING_ALREADY_DELETED_DATA_NOT_ALLOWED.buildMessage()); } if (writeSet.containsKey(key)) { // merge the previous put in the write set and the new put @@ -167,7 +169,8 @@ public Optional get(Key key) throws CrudException { } } throw new IllegalArgumentException( - "Getting data neither in the read set nor the delete set is not allowed"); + CoreError.CONSENSUS_COMMIT_GETTING_DATA_NEITHER_IN_READ_SET_NOR_DELETE_SET_NOT_ALLOWED + .buildMessage()); } private TableMetadata getTableMetadata(Key key) throws CrudException { @@ -176,29 +179,22 @@ private TableMetadata getTableMetadata(Key key) throws CrudException { tableMetadataManager.getTransactionTableMetadata(key.getNamespace(), key.getTable()); if (metadata == null) { throw new IllegalArgumentException( - "The specified table is not found: " - + ScalarDbUtils.getFullTableName(key.getNamespace(), key.getTable())); + CoreError.TABLE_NOT_FOUND.buildMessage( + ScalarDbUtils.getFullTableName(key.getNamespace(), key.getTable()))); } return metadata.getTableMetadata(); } catch (ExecutionException e) { - throw new CrudException("Getting a table metadata failed", e, id); + throw new CrudException(e.getMessage(), e, id); } } private TableMetadata getTableMetadata(Scan scan) throws ExecutionException { - try { - TransactionTableMetadata metadata = - tableMetadataManager.getTransactionTableMetadata( - scan.forNamespace().get(), scan.forTable().get()); - if (metadata == null) { - throw new IllegalArgumentException( - "The specified table is not found: " - + ScalarDbUtils.getFullTableName(scan.forNamespace().get(), scan.forTable().get())); - } - return metadata.getTableMetadata(); - } catch (ExecutionException e) { - throw new ExecutionException("Getting a table metadata failed", e); + TransactionTableMetadata metadata = tableMetadataManager.getTransactionTableMetadata(scan); + if (metadata == null) { + throw new IllegalArgumentException( + CoreError.TABLE_NOT_FOUND.buildMessage(scan.forFullTableName().get())); } + return metadata.getTableMetadata(); } public Optional> get(Scan scan) { @@ -210,7 +206,8 @@ public Optional> get(Scan scan) { public void verify(Scan scan) { if (isWriteSetOverlappedWith(scan)) { - throw new IllegalArgumentException("Reading already written data is not allowed"); + throw new IllegalArgumentException( + CoreError.CONSENSUS_COMMIT_READING_ALREADY_WRITTEN_DATA_NOT_ALLOWED.buildMessage()); } } @@ -365,7 +362,7 @@ private boolean match(Column column, ConditionalExpression condition) { case NOT_LIKE: return isMatched((LikeExpression) condition, column.getTextValue()); default: - throw new IllegalArgumentException("Unknown operator: " + condition.getOperator()); + throw new AssertionError("Unknown operator: " + condition.getOperator()); } } @@ -523,12 +520,12 @@ private boolean isChanged( private void throwExceptionDueToPotentialAntiDependency() throws PreparationConflictException { throw new PreparationConflictException( - "Reading empty records might cause write skew anomaly so aborting the transaction for safety", - id); + CoreError.CONSENSUS_COMMIT_READING_EMPTY_RECORDS_IN_EXTRA_WRITE.buildMessage(), id); } private void throwExceptionDueToAntiDependency() throws ValidationConflictException { - throw new ValidationConflictException("Anti-dependency found. Aborting the transaction", id); + throw new ValidationConflictException( + CoreError.CONSENSUS_COMMIT_ANTI_DEPENDENCY_FOUND_IN_EXTRA_READ.buildMessage(), id); } private boolean isExtraReadEnabled() { diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/TransactionTableMetadataManager.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/TransactionTableMetadataManager.java index 8e2109c546..e227736134 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/TransactionTableMetadataManager.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/TransactionTableMetadataManager.java @@ -6,7 +6,9 @@ import com.scalar.db.api.DistributedStorageAdmin; import com.scalar.db.api.Operation; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; +import com.scalar.db.util.ScalarDbUtils; import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -51,7 +53,8 @@ public Optional load(@Nonnull TableKey key) public TransactionTableMetadata getTransactionTableMetadata(Operation operation) throws ExecutionException { if (!operation.forNamespace().isPresent() || !operation.forTable().isPresent()) { - throw new IllegalArgumentException("Operation has no target namespace and table name"); + throw new IllegalArgumentException( + CoreError.OPERATION_DOES_NOT_HAVE_TARGET_NAMESPACE_OR_TABLE_NAME.buildMessage(operation)); } return getTransactionTableMetadata(operation.forNamespace().get(), operation.forTable().get()); } @@ -70,7 +73,10 @@ public TransactionTableMetadata getTransactionTableMetadata(String namespace, St TableKey key = new TableKey(namespace, table); return tableMetadataCache.get(key).orElse(null); } catch (java.util.concurrent.ExecutionException e) { - throw new ExecutionException("Getting a table metadata failed", e); + throw new ExecutionException( + CoreError.GETTING_TABLE_METADATA_FAILED.buildMessage( + ScalarDbUtils.getFullTableName(namespace, table)), + e); } } diff --git a/core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommit.java b/core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommit.java index e46e14d1ee..a79c7ac94f 100644 --- a/core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommit.java +++ b/core/src/main/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommit.java @@ -11,8 +11,9 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.TransactionState; import com.scalar.db.common.AbstractTwoPhaseCommitTransaction; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; -import com.scalar.db.exception.transaction.CommitException; +import com.scalar.db.exception.transaction.CommitConflictException; import com.scalar.db.exception.transaction.CrudConflictException; import com.scalar.db.exception.transaction.CrudException; import com.scalar.db.exception.transaction.PreparationConflictException; @@ -140,9 +141,12 @@ public void prepare() throws PreparationException { lazyRecovery((UncommittedRecordException) e); } throw new PreparationConflictException( - "Conflict occurred while implicit pre-read", e, getId()); + CoreError.CONSENSUS_COMMIT_CONFLICT_OCCURRED_WHILE_IMPLICIT_PRE_READ.buildMessage(), + e, + getId()); } catch (CrudException e) { - throw new PreparationException("Failed to execute implicit pre-read", e, getId()); + throw new PreparationException( + CoreError.CONSENSUS_COMMIT_EXECUTING_IMPLICIT_PRE_READ_FAILED.buildMessage(), e, getId()); } try { @@ -159,17 +163,15 @@ public void validate() throws ValidationException { } @Override - public void commit() throws CommitException, UnknownTransactionStatusException { + public void commit() throws CommitConflictException, UnknownTransactionStatusException { if (crud.getSnapshot().isValidationRequired() && !validated) { throw new IllegalStateException( - "The transaction is not validated." - + " When using the EXTRA_READ serializable strategy, you need to call validate()" - + " before calling commit()"); + CoreError.CONSENSUS_COMMIT_TRANSACTION_NOT_VALIDATED_IN_EXTRA_READ.buildMessage()); } try { commit.commitState(crud.getSnapshot()); - } catch (CommitException | UnknownTransactionStatusException e) { + } catch (CommitConflictException | UnknownTransactionStatusException e) { // no need to rollback because the transaction has already been rolled back needRollback = false; @@ -189,10 +191,13 @@ public void rollback() throws RollbackException { TransactionState state = commit.abortState(crud.getSnapshot().getId()); if (state == TransactionState.COMMITTED) { throw new RollbackException( - "Rollback failed because the transaction has already been committed", getId()); + CoreError.CONSENSUS_COMMIT_ROLLBACK_FAILED_BECAUSE_TRANSACTION_ALREADY_COMMITTED + .buildMessage(), + getId()); } } catch (UnknownTransactionStatusException e) { - throw new RollbackException("Rollback failed", e, getId()); + throw new RollbackException( + CoreError.CONSENSUS_COMMIT_ROLLBACK_FAILED.buildMessage(), e, getId()); } commit.rollbackRecords(crud.getSnapshot()); @@ -228,7 +233,7 @@ private void checkMutation(Mutation mutation) throws CrudException { try { mutationOperationChecker.check(mutation); } catch (ExecutionException e) { - throw new CrudException("Checking the operation failed", e, getId()); + throw new CrudException(e.getMessage(), e, getId()); } } } diff --git a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransaction.java b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransaction.java index ad4491ea21..a18c7578e3 100644 --- a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransaction.java +++ b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransaction.java @@ -13,6 +13,7 @@ import com.scalar.db.api.Result; import com.scalar.db.api.Scan; import com.scalar.db.common.AbstractDistributedTransaction; +import com.scalar.db.common.error.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.exception.transaction.CommitConflictException; import com.scalar.db.exception.transaction.CommitException; @@ -65,9 +66,10 @@ public Optional get(Get get) throws CrudException { try { return jdbcService.get(get, connection); } catch (SQLException e) { - throw createCrudException(e, "Get operation failed"); + throw createCrudException(e, CoreError.JDBC_TRANSACTION_GET_OPERATION_FAILED.buildMessage()); } catch (ExecutionException e) { - throw new CrudException("Get operation failed", e, txId); + throw new CrudException( + CoreError.JDBC_TRANSACTION_GET_OPERATION_FAILED.buildMessage(), e, txId); } } @@ -77,9 +79,10 @@ public List scan(Scan scan) throws CrudException { try { return jdbcService.scan(scan, connection); } catch (SQLException e) { - throw createCrudException(e, "Scan operation failed"); + throw createCrudException(e, CoreError.JDBC_TRANSACTION_SCAN_OPERATION_FAILED.buildMessage()); } catch (ExecutionException e) { - throw new CrudException("Scan operation failed", e, txId); + throw new CrudException( + CoreError.JDBC_TRANSACTION_SCAN_OPERATION_FAILED.buildMessage(), e, txId); } } @@ -92,18 +95,16 @@ public void put(Put put) throws CrudException { throwUnsatisfiedConditionException(put); } } catch (SQLException e) { - throw createCrudException(e, "Put operation failed"); + throw createCrudException(e, CoreError.JDBC_TRANSACTION_PUT_OPERATION_FAILED.buildMessage()); } catch (ExecutionException e) { - throw new CrudException("Put operation failed", e, txId); + throw new CrudException( + CoreError.JDBC_TRANSACTION_PUT_OPERATION_FAILED.buildMessage(), e, txId); } } @Override public void put(List puts) throws CrudException { - checkArgument(puts.size() != 0); - for (Put put : puts) { - put(put); - } + mutate(puts); } @Override @@ -115,23 +116,22 @@ public void delete(Delete delete) throws CrudException { throwUnsatisfiedConditionException(delete); } } catch (SQLException e) { - throw createCrudException(e, "Delete operation failed"); + throw createCrudException( + e, CoreError.JDBC_TRANSACTION_DELETE_OPERATION_FAILED.buildMessage()); } catch (ExecutionException e) { - throw new CrudException("Delete operation failed", e, txId); + throw new CrudException( + CoreError.JDBC_TRANSACTION_DELETE_OPERATION_FAILED.buildMessage(), e, txId); } } @Override public void delete(List deletes) throws CrudException { - checkArgument(deletes.size() != 0); - for (Delete delete : deletes) { - delete(delete); - } + mutate(deletes); } @Override public void mutate(List mutations) throws CrudException { - checkArgument(mutations.size() != 0); + checkArgument(!mutations.isEmpty(), CoreError.EMPTY_MUTATIONS_SPECIFIED.buildMessage()); for (Mutation mutation : mutations) { if (mutation instanceof Put) { put((Put) mutation); @@ -149,7 +149,10 @@ public void commit() throws CommitException, UnknownTransactionStatusException { try { connection.rollback(); } catch (SQLException sqlException) { - throw new UnknownTransactionStatusException("Failed to rollback", sqlException, txId); + throw new UnknownTransactionStatusException( + CoreError.JDBC_TRANSACTION_UNKNOWN_TRANSACTION_STATUS.buildMessage(), + sqlException, + txId); } throw createCommitException(e); } finally { @@ -177,24 +180,12 @@ private void throwUnsatisfiedConditionException(Mutation mutation) .collect(Collectors.joining(", ")); } - StringBuilder exceptionMessage = - new StringBuilder("The ") - .append(condition.getClass().getSimpleName()) - .append(" condition "); - if (conditionColumns != null) { - // To write 'column' in the plural or singular form - if (condition.getExpressions().size() > 1) { - exceptionMessage.append("targeting the columns '").append(conditionColumns).append("' "); - } else { - exceptionMessage.append("targeting the column '").append(conditionColumns).append("' "); - } - } - exceptionMessage - .append("of the ") - .append(mutation.getClass().getSimpleName()) - .append(" operation is not satisfied"); - - throw new UnsatisfiedConditionException(exceptionMessage.toString(), txId); + throw new UnsatisfiedConditionException( + CoreError.JDBC_TRANSACTION_CONDITION_NOT_SATISFIED.buildMessage( + condition.getClass().getSimpleName(), + mutation.getClass().getSimpleName(), + conditionColumns == null ? "null" : "[" + conditionColumns + "]"), + txId); } @Override @@ -207,7 +198,8 @@ public void rollback() throws RollbackException { connection.rollback(); } catch (SQLException e) { - throw new RollbackException("Failed to rollback", e, txId); + throw new RollbackException( + CoreError.JDBC_TRANSACTION_ROLLING_BACK_TRANSACTION_FAILED.buildMessage(), e, txId); } finally { try { connection.close(); @@ -218,16 +210,19 @@ public void rollback() throws RollbackException { } private CrudException createCrudException(SQLException e, String message) { - if (rdbEngine.isConflictError(e)) { - return new CrudConflictException("Conflict happened; try restarting transaction", e, txId); + if (rdbEngine.isConflict(e)) { + return new CrudConflictException( + CoreError.JDBC_TRANSACTION_CONFLICT_OCCURRED.buildMessage(), e, txId); } return new CrudException(message, e, txId); } private CommitException createCommitException(SQLException e) { - if (rdbEngine.isConflictError(e)) { - return new CommitConflictException("Conflict happened; try restarting transaction", e, txId); + if (rdbEngine.isConflict(e)) { + return new CommitConflictException( + CoreError.JDBC_TRANSACTION_CONFLICT_OCCURRED.buildMessage(), e, txId); } - return new CommitException("Failed to commit", e, txId); + return new CommitException( + CoreError.JDBC_TRANSACTION_COMMITTING_TRANSACTION_FAILED.buildMessage(), e, txId); } } diff --git a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionAdmin.java b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionAdmin.java index e47f03ef66..4e2e5fe3db 100644 --- a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionAdmin.java +++ b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionAdmin.java @@ -139,12 +139,14 @@ public Set getNamespaceNames() throws ExecutionException { @Override public void repairNamespace(String namespace, Map options) throws ExecutionException { + // TODO: implement later throw new UnsupportedOperationException( "Repairing a namespace is not supported with the JDBC transaction admin"); } @Override public void upgrade(Map options) throws ExecutionException { + // TODO: implement later throw new UnsupportedOperationException( "Upgrading the ScalarDB environment is not supported with the JDBC transaction admin"); } diff --git a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionManager.java b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionManager.java index e6d6b2c41e..696f940cbd 100644 --- a/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionManager.java +++ b/core/src/main/java/com/scalar/db/transaction/jdbc/JdbcTransactionManager.java @@ -9,6 +9,7 @@ import com.scalar.db.common.ActiveTransactionManagedDistributedTransactionManager; import com.scalar.db.common.TableMetadataManager; import com.scalar.db.common.checker.OperationChecker; +import com.scalar.db.common.error.CoreError; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.exception.transaction.TransactionException; import com.scalar.db.storage.jdbc.JdbcAdmin; @@ -82,7 +83,8 @@ public DistributedTransaction begin(String txId) throws TransactionException { getTable().ifPresent(transaction::withTable); return decorate(transaction); } catch (SQLException e) { - throw new TransactionException("Failed to start the transaction", e, null); + throw new TransactionException( + CoreError.JDBC_TRANSACTION_BEGINNING_TRANSACTION_FAILED.buildMessage(), e, null); } } @@ -140,12 +142,14 @@ public DistributedTransaction start( @Override public TransactionState getState(String txId) { - throw new UnsupportedOperationException("This method is not supported in JDBC transaction"); + throw new UnsupportedOperationException( + CoreError.JDBC_TRANSACTION_GETTING_TRANSACTION_STATE_NOT_SUPPORTED.buildMessage()); } @Override public TransactionState rollback(String txId) { - throw new UnsupportedOperationException("This method is not supported in JDBC transaction"); + throw new UnsupportedOperationException( + CoreError.JDBC_TRANSACTION_ROLLING_BACK_TRANSACTION_NOT_SUPPORTED.buildMessage()); } @Override diff --git a/core/src/main/java/com/scalar/db/util/ScalarDbUtils.java b/core/src/main/java/com/scalar/db/util/ScalarDbUtils.java index 4db72c32ab..0db4421551 100644 --- a/core/src/main/java/com/scalar/db/util/ScalarDbUtils.java +++ b/core/src/main/java/com/scalar/db/util/ScalarDbUtils.java @@ -12,6 +12,7 @@ import com.scalar.db.api.ScanWithIndex; import com.scalar.db.api.Selection; import com.scalar.db.api.TableMetadata; +import com.scalar.db.common.error.CoreError; import com.scalar.db.io.BigIntColumn; import com.scalar.db.io.BigIntValue; import com.scalar.db.io.BlobColumn; @@ -109,7 +110,8 @@ private static void setTargetToIfNot( operation.forTable(tableName.orElse(null)); } if (!operation.forNamespace().isPresent() || !operation.forTable().isPresent()) { - throw new IllegalArgumentException("Operation has no target namespace and table name"); + throw new IllegalArgumentException( + CoreError.OPERATION_DOES_NOT_HAVE_TARGET_NAMESPACE_OR_TABLE_NAME.buildMessage(operation)); } } diff --git a/core/src/test/java/com/scalar/db/common/error/CoreErrorTest.java b/core/src/test/java/com/scalar/db/common/error/CoreErrorTest.java new file mode 100644 index 0000000000..00026b2d4d --- /dev/null +++ b/core/src/test/java/com/scalar/db/common/error/CoreErrorTest.java @@ -0,0 +1,48 @@ +package com.scalar.db.common.error; + +import com.scalar.db.api.Put; +import com.scalar.db.io.Key; +import java.util.Arrays; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CoreErrorTest { + + @Test + public void checkDuplicateErrorCode() { + Assertions.assertThat(Arrays.stream(CoreError.values()).map(CoreError::buildCode)) + .doesNotHaveDuplicates(); + } + + @Test + public void buildCode_ShouldBuildCorrectCode() { + // Arrange + CoreError error = CoreError.OPERATION_CHECK_ERROR_INDEX_ONLY_SINGLE_COLUMN_INDEX_SUPPORTED; + + // Act + String code = error.buildCode(); + + // Assert + Assertions.assertThat(code).isEqualTo("CORE-10000"); + } + + @Test + public void buildMessage_ShouldBuildCorrectMessage() { + // Arrange + CoreError error = CoreError.OPERATION_CHECK_ERROR_INDEX_ONLY_SINGLE_COLUMN_INDEX_SUPPORTED; + Put put = + Put.newBuilder() + .namespace("ns") + .table("tbl") + .partitionKey(Key.ofInt("id", 0)) + .intValue("col", 0) + .build(); + + // Act + String message = error.buildMessage(put); + + // Assert + Assertions.assertThat(message) + .isEqualTo("CORE-10000: Only a single-column index is supported. Operation: " + put); + } +} diff --git a/core/src/test/java/com/scalar/db/storage/cassandra/StatementHandlerManagerTest.java b/core/src/test/java/com/scalar/db/storage/cassandra/StatementHandlerManagerTest.java index 2e6f8d10f3..beee29b700 100644 --- a/core/src/test/java/com/scalar/db/storage/cassandra/StatementHandlerManagerTest.java +++ b/core/src/test/java/com/scalar/db/storage/cassandra/StatementHandlerManagerTest.java @@ -72,7 +72,7 @@ public void build_SomeHandlerNotGiven_ShouldNotInstantiate() { .insert(insert) .update(update) .build()) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalStateException.class); } @Test diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java index 76bf372baf..382054ef29 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java @@ -443,7 +443,7 @@ public void createTableInternal_ForSqlite_withInvalidTableName_ShouldThrowExecut () -> admin.createTableInternal( mock(Connection.class), namespace, table, metadata, false)) - .isInstanceOf(ExecutionException.class); + .isInstanceOf(IllegalArgumentException.class); } @Test diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineSqliteTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineSqliteTest.java index a9f71fa386..516eb6d89d 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineSqliteTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineSqliteTest.java @@ -96,7 +96,7 @@ void isUndefinedTableError_False() { } @Test - void isConflictError_True_SQLITE_BUSY_WhenUpdatingBeingDeletedTableInSeparateConnection() + void isConflict_True_SQLITE_BUSY_WhenUpdatingBeingDeletedTableInSeparateConnection() throws SQLException { statement.executeUpdate("create table t (c integer)"); statement.executeUpdate("insert into t values (1)"); @@ -112,12 +112,12 @@ void isConflictError_True_SQLITE_BUSY_WhenUpdatingBeingDeletedTableInSeparateCon SQLException e = (SQLException) catchThrowable(() -> statement2.executeUpdate("update t set c = c + 1 where c = 1")); - assertTrue(rdbEngine.isConflictError(e)); + assertTrue(rdbEngine.isConflict(e)); } @Test - void isConflictError_False() { - assertFalse(rdbEngine.isConflictError(causeSyntaxError())); + void isConflict_False() { + assertFalse(rdbEngine.isConflict(causeSyntaxError())); } @Test diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationCheckerTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationCheckerTest.java index 8367bfa5e5..330703f0b5 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationCheckerTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/ConsensusCommitMutationOperationCheckerTest.java @@ -82,12 +82,15 @@ public void checkForPut_WithAllowedCondition_ShouldCallConditionChecker( public void checkForPut_ThatMutatesMetadataColumns_ShouldThrowIllegalArgumentException() throws ExecutionException { // Arrange + String fullTableName = "ns.tbl"; Set columns = ImmutableSet.of(ANY_COL_1, ANY_METADATA_COL_1, ANY_COL_2); + when(put.forFullTableName()).thenReturn(Optional.of(fullTableName)); when(put.getContainedColumnNames()).thenReturn(columns); // Act Assert Assertions.assertThatThrownBy(() -> checker.check(put)) .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(fullTableName) .hasMessageContaining(ANY_METADATA_COL_1); verify(metadataManager).getTransactionTableMetadata(put); } diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManagerTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManagerTest.java index c5b3154640..cfda9509df 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManagerTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitManagerTest.java @@ -13,6 +13,7 @@ import com.scalar.db.api.TwoPhaseCommitTransaction; import com.scalar.db.common.DecoratedTwoPhaseCommitTransaction; import com.scalar.db.config.DatabaseConfig; +import com.scalar.db.exception.transaction.CommitConflictException; import com.scalar.db.exception.transaction.CommitException; import com.scalar.db.exception.transaction.RollbackException; import com.scalar.db.exception.transaction.TransactionException; @@ -274,7 +275,7 @@ public void resume_CalledWithBeginAndCommit_ThrowTransactionNotFoundException() public void resume_CalledWithBeginAndCommit_CommitExceptionThrown_ReturnSameTransactionObject() throws TransactionException { // Arrange - doThrow(CommitException.class).when(commit).commitState(any()); + doThrow(CommitConflictException.class).when(commit).commitState(any()); TwoPhaseCommitTransaction transaction1 = manager.begin(ANY_TX_ID); transaction1.prepare(); diff --git a/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitTest.java b/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitTest.java index 827519c413..a147a06efa 100644 --- a/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitTest.java +++ b/core/src/test/java/com/scalar/db/transaction/consensuscommit/TwoPhaseConsensusCommitTest.java @@ -16,6 +16,7 @@ import com.scalar.db.api.Scan; import com.scalar.db.api.TransactionState; import com.scalar.db.exception.storage.ExecutionException; +import com.scalar.db.exception.transaction.CommitConflictException; import com.scalar.db.exception.transaction.CommitException; import com.scalar.db.exception.transaction.CrudConflictException; import com.scalar.db.exception.transaction.CrudException; @@ -393,7 +394,7 @@ public void rollback_CalledAfterCommitFails_ShouldNeverAbortStateAndRollbackReco // Arrange transaction.prepare(); when(crud.getSnapshot()).thenReturn(snapshot); - doThrow(CommitException.class).when(commit).commitState(snapshot); + doThrow(CommitConflictException.class).when(commit).commitState(snapshot); // Act assertThatThrownBy(transaction::commit).isInstanceOf(CommitException.class); diff --git a/core/src/test/java/com/scalar/db/transaction/jdbc/JdbcTransactionTest.java b/core/src/test/java/com/scalar/db/transaction/jdbc/JdbcTransactionTest.java index e7514cc436..adedf255ba 100644 --- a/core/src/test/java/com/scalar/db/transaction/jdbc/JdbcTransactionTest.java +++ b/core/src/test/java/com/scalar/db/transaction/jdbc/JdbcTransactionTest.java @@ -120,18 +120,18 @@ private static Stream provideConditionalPuts() { return Stream.of( Arguments.of( ConditionBuilder.putIf(ConditionBuilder.column("c1").isNullText()).build(), - "The PutIf condition targeting the column 'c1' of the Put operation is not satisfied"), + "The PutIf condition of the Put operation is not satisfied. Targeting column(s): [c1]"), Arguments.of( ConditionBuilder.putIf(ConditionBuilder.column("c1").isNullText()) .and(ConditionBuilder.column("c2").isEqualToText("a")) .build(), - "The PutIf condition targeting the columns 'c1, c2' of the Put operation is not satisfied"), + "The PutIf condition of the Put operation is not satisfied. Targeting column(s): [c1, c2]"), Arguments.of( ConditionBuilder.putIfExists(), - "The PutIfExists condition of the Put operation is not satisfied"), + "The PutIfExists condition of the Put operation is not satisfied. Targeting column(s): null"), Arguments.of( ConditionBuilder.putIfNotExists(), - "The PutIfNotExists condition of the Put operation is not satisfied")); + "The PutIfNotExists condition of the Put operation is not satisfied. Targeting column(s): null")); } @ParameterizedTest @@ -155,14 +155,14 @@ private static Stream provideConditionalDeletes() { return Stream.of( Arguments.of( ConditionBuilder.deleteIf(ConditionBuilder.column("c1").isNullText()).build(), - "The DeleteIf condition targeting the column 'c1' of the Delete operation is not satisfied"), + "The DeleteIf condition of the Delete operation is not satisfied. Targeting column(s): [c1]"), Arguments.of( ConditionBuilder.deleteIf(ConditionBuilder.column("c1").isNullText()) .and(ConditionBuilder.column("c2").isEqualToText("a")) .build(), - "The DeleteIf condition targeting the columns 'c1, c2' of the Delete operation is not satisfied"), + "The DeleteIf condition of the Delete operation is not satisfied. Targeting column(s): [c1, c2]"), Arguments.of( ConditionBuilder.deleteIfExists(), - "The DeleteIfExists condition of the Delete operation is not satisfied")); + "The DeleteIfExists condition of the Delete operation is not satisfied. Targeting column(s): null")); } }