This is an automated email from the ASF dual-hosted git repository.
rpuch pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new c7f234ba77 IGNITE-20966 Improve error messages for schema change
validation failures (#3805)
c7f234ba77 is described below
commit c7f234ba77d039011b1531890be546926fb13bbb
Author: Phillippko <[email protected]>
AuthorDate: Tue May 28 14:39:43 2024 +0400
IGNITE-20966 Improve error messages for schema change validation failures
(#3805)
---
.../ItSchemaForwardCompatibilityTest.java | 19 ++-
.../schemasync/ItSchemaSyncSingleNodeTest.java | 14 +-
.../replicator/CompatValidationResult.java | 56 ++++--
.../replicator/IncompatibleSchemaException.java | 43 +++++
.../replicator/PartitionReplicaListener.java | 19 ++-
.../replicator/SchemaCompatibilityValidator.java | 153 +++++++++++------
.../distributed/schema/ColumnDefinitionDiff.java | 4 +
.../replication/PartitionReplicaListenerTest.java | 26 ++-
.../SchemaCompatibilityValidatorTest.java | 187 +++++++++++++++------
9 files changed, 373 insertions(+), 148 deletions(-)
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaForwardCompatibilityTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaForwardCompatibilityTest.java
index d8409314a9..47d2e0054e 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaForwardCompatibilityTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaForwardCompatibilityTest.java
@@ -107,8 +107,10 @@ class ItSchemaForwardCompatibilityTest extends
ClusterPerTestIntegrationTest {
assertThat(
ex.getMessage(),
containsString(String.format(
- "Commit failed because schema 1 is not
forward-compatible with 2 for table %d",
- tableId
+ "Commit failed because schema is not
forward-compatible [fromSchemaVersion=1, toSchemaVersion=2, table=%s, "
+ + "details=%s]",
+ table.name(),
+ ddl.expectedDetails
))
);
@@ -154,15 +156,18 @@ class ItSchemaForwardCompatibilityTest extends
ClusterPerTestIntegrationTest {
private enum ForwardIncompatibleDdl {
// TODO: Enable after
https://issues.apache.org/jira/browse/IGNITE-19484 is fixed.
// RENAME_TABLE("RENAME TABLE " + TABLE_NAME + " to new_table"),
- DROP_COLUMN("ALTER TABLE " + TABLE_NAME + " DROP COLUMN not_null_int"),
- ADD_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN not_null_int
SET DEFAULT 102"),
- CHANGE_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN
int_with_default SET DEFAULT 102"),
- DROP_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN
int_with_default DROP DEFAULT");
+ DROP_COLUMN("ALTER TABLE " + TABLE_NAME + " DROP COLUMN not_null_int",
"Columns were dropped"),
+ ADD_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN not_null_int
SET DEFAULT 102", "Column default value changed"),
+ CHANGE_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN
int_with_default SET DEFAULT 102",
+ "Column default value changed"),
+ DROP_DEFAULT("ALTER TABLE " + TABLE_NAME + " ALTER COLUMN
int_with_default DROP DEFAULT", "Column default value changed");
private final String ddl;
+ private final String expectedDetails;
- ForwardIncompatibleDdl(String ddl) {
+ ForwardIncompatibleDdl(String ddl, String expectedDetails) {
this.ddl = ddl;
+ this.expectedDetails = expectedDetails;
}
void executeOn(Cluster cluster) {
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaSyncSingleNodeTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaSyncSingleNodeTest.java
index 87d12e8601..cb37e8f382 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaSyncSingleNodeTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/schemasync/ItSchemaSyncSingleNodeTest.java
@@ -110,7 +110,7 @@ class ItSchemaSyncSingleNodeTest extends
ClusterPerTestIntegrationTest {
ex.getMessage(),
containsString(String.format(
"Table schema was updated after the transaction
was started [table=%s, startSchema=1, operationSchema=2]",
- tableId
+ table.name()
))
);
} else {
@@ -119,7 +119,7 @@ class ItSchemaSyncSingleNodeTest extends
ClusterPerTestIntegrationTest {
ex.getMessage(),
is(String.format(
"Table schema was updated after the transaction
was started [table=%s, startSchema=1, operationSchema=2]",
- tableId
+ table.name()
))
);
}
@@ -252,13 +252,15 @@ class ItSchemaSyncSingleNodeTest extends
ClusterPerTestIntegrationTest {
ex = assertThrows(IgniteException.class, () ->
operation.execute(table, tx, cluster));
assertThat(
ex.getMessage(),
- containsString(String.format("Table was dropped
[table=%s]", tableId))
+ // TODO https://issues.apache.org/jira/browse/IGNITE-22309
use tableName instead
+ containsString(String.format("Table was dropped
[tableId=%s]", tableId))
);
} else {
ex = assertThrows(IncompatibleSchemaException.class, () ->
operation.execute(table, tx, cluster));
assertThat(
ex.getMessage(),
- is(String.format("Table was dropped [table=%s]", tableId))
+ // TODO https://issues.apache.org/jira/browse/IGNITE-22309
use tableName instead
+ is(String.format("Table was dropped [tableId=%s]",
tableId))
);
}
@@ -294,7 +296,7 @@ class ItSchemaSyncSingleNodeTest extends
ClusterPerTestIntegrationTest {
assertThat(ex, is(instanceOf(TransactionException.class)));
assertThat(
ex.getMessage(),
- containsString(String.format("Commit failed because a table
was already dropped [tableId=%s]", tableId))
+ containsString(String.format("Commit failed because a table
was already dropped [table=%s]", table.name()))
);
assertThat(((TransactionException) ex).code(),
is(Transactions.TX_UNEXPECTED_STATE_ERR));
@@ -356,7 +358,7 @@ class ItSchemaSyncSingleNodeTest extends
ClusterPerTestIntegrationTest {
containsString(String.format(
"Operation failed because it tried to access a row
with newer schema version than transaction's [table=%s, "
+ "txSchemaVersion=1, rowSchemaVersion=2]",
- tableId
+ table.name()
))
);
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/CompatValidationResult.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/CompatValidationResult.java
index d4a00ed5d3..984ac4730c 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/CompatValidationResult.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/CompatValidationResult.java
@@ -23,16 +23,17 @@ import org.jetbrains.annotations.Nullable;
* Result of a schema compatibility validation.
*/
public class CompatValidationResult {
- private static final CompatValidationResult SUCCESSFUL_RESULT = new
CompatValidationResult(ValidationStatus.SUCCESS, -1, -1, -1);
+ private static final CompatValidationResult SUCCESSFUL_RESULT = new
CompatValidationResult(ValidationStatus.SUCCESS, "", -1, -1, null);
private enum ValidationStatus {
SUCCESS, INCOMPATIBLE_CHANGE, TABLE_DROPPED
}
private final ValidationStatus status;
- private final int failedTableId;
+ private final String failedTableName;
private final int fromSchemaVersion;
private final @Nullable Integer toSchemaVersion;
+ private final @Nullable String details;
/**
* Returns a successful validation result.
@@ -46,36 +47,49 @@ public class CompatValidationResult {
/**
* Creates a validation result denoting incompatible schema change.
*
- * @param failedTableId Table which schema change is incompatible.
+ * @param failedTableName Table which schema change is incompatible.
* @param fromSchemaVersion Version number of the schema from which an
incompatible transition tried to be made.
* @param toSchemaVersion Version number of the schema to which an
incompatible transition tried to be made.
* @return A validation result for a failure.
*/
- public static CompatValidationResult incompatibleChange(int failedTableId,
int fromSchemaVersion, int toSchemaVersion) {
- return new
CompatValidationResult(ValidationStatus.INCOMPATIBLE_CHANGE, failedTableId,
fromSchemaVersion, toSchemaVersion);
+ public static CompatValidationResult incompatibleChange(
+ String failedTableName,
+ int fromSchemaVersion,
+ int toSchemaVersion,
+ @Nullable String details
+ ) {
+ return new CompatValidationResult(
+ ValidationStatus.INCOMPATIBLE_CHANGE,
+ failedTableName,
+ fromSchemaVersion,
+ toSchemaVersion,
+ details
+ );
}
/**
* Creates a validation result denoting 'table already dropped when commit
is made' situation.
*
- * @param failedTableId Table which schema change is incompatible.
+ * @param failedTableName Table which schema change is incompatible.
* @param fromSchemaVersion Version number of the schema from which an
incompatible transition tried to be made.
* @return A validation result for a failure.
*/
- public static CompatValidationResult tableDropped(int failedTableId, int
fromSchemaVersion) {
- return new CompatValidationResult(ValidationStatus.TABLE_DROPPED,
failedTableId, fromSchemaVersion, null);
+ public static CompatValidationResult tableDropped(String failedTableName,
int fromSchemaVersion) {
+ return new CompatValidationResult(ValidationStatus.TABLE_DROPPED,
failedTableName, fromSchemaVersion, null, null);
}
private CompatValidationResult(
ValidationStatus status,
- int failedTableId,
+ String failedTableName,
int fromSchemaVersion,
- @Nullable Integer toSchemaVersion
+ @Nullable Integer toSchemaVersion,
+ @Nullable String details
) {
this.status = status;
- this.failedTableId = failedTableId;
+ this.failedTableName = failedTableName;
this.fromSchemaVersion = fromSchemaVersion;
this.toSchemaVersion = toSchemaVersion;
+ this.details = details;
}
/**
@@ -95,15 +109,15 @@ public class CompatValidationResult {
}
/**
- * Returns ID of the table for which the validation has failed. Should
only be called for a failed validation result, otherwise an
+ * Returns name of the table for which the validation has failed. Should
only be called for a failed validation result, otherwise an
* exception is thrown.
*
- * @return Table ID.
+ * @return Table name.
*/
- public int failedTableId() {
+ public String failedTableName() {
assert !isSuccessful() : "Should not be called on a successful result";
- return failedTableId;
+ return failedTableName;
}
/**
@@ -128,4 +142,16 @@ public class CompatValidationResult {
return toSchemaVersion;
}
+
+ /**
+ * For failed validation returns details why the operation failed.
+ *
+ * @return Details why the operation failed.
+ */
+ public String details() {
+ assert !isSuccessful() : "Should not be called on a successful result";
+ assert details != null : "Should not be called when there are no
details";
+
+ return details;
+ }
}
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/IncompatibleSchemaException.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/IncompatibleSchemaException.java
index 04480f784c..5c4f0027f8 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/IncompatibleSchemaException.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/IncompatibleSchemaException.java
@@ -26,7 +26,50 @@ import org.apache.ignite.tx.TransactionException;
* because an incompatible schema change has happened.
*/
public class IncompatibleSchemaException extends TransactionException
implements ExpectedReplicationException {
+ private static final String SCHEMA_CHANGED_MESSAGE = "Table schema was
updated after the transaction was started "
+ + "[table=%s, startSchema=%d, operationSchema=%d]";
+
+ private static final String TABLE_DROPPED_NAME_MESSAGE = "Table was
dropped [table=%s]";
+
+ private static final String TABLE_DROPPED_ID_MESSAGE = "Table was dropped
[tableId=%d]";
+
public IncompatibleSchemaException(String message) {
super(Transactions.TX_INCOMPATIBLE_SCHEMA_ERR, message);
}
+
+ /**
+ * Returns new IncompatibleSchemaException for a case when schema was
updated after the beginning of the transaction.
+ *
+ * @param tableName Name of the table.
+ * @param startSchemaVersion Schema version at the beginning of the
transaction.
+ * @param operationSchemaVersion Schema version at the moment of the
operation.
+ * @return Exception with formatted message.
+ */
+ public static IncompatibleSchemaException schemaChanged(String tableName,
int startSchemaVersion, int operationSchemaVersion) {
+ return new IncompatibleSchemaException(String.format(
+ SCHEMA_CHANGED_MESSAGE,
+ tableName, startSchemaVersion, operationSchemaVersion
+ ));
+ }
+
+ /**
+ * Returns new IncompatibleSchemaException for a case when the table was
dropped at the moment of operation.
+ *
+ * @param tableName Name of the table.
+ * @return Exception with formatted message.
+ */
+ public static IncompatibleSchemaException tableDropped(String tableName) {
+ return new
IncompatibleSchemaException(String.format(TABLE_DROPPED_NAME_MESSAGE,
tableName));
+ }
+
+ /**
+ * Returns new IncompatibleSchemaException for a case when table was
dropped at the moment of operation.
+ *
+ * @param tableId ID of the table.
+ * @return Exception with formatted message.
+ */
+ // TODO https://issues.apache.org/jira/browse/IGNITE-22309 use tableName
instead
+ public static IncompatibleSchemaException tableDropped(int tableId) {
+ return new
IncompatibleSchemaException(String.format(TABLE_DROPPED_ID_MESSAGE, tableId));
+ }
}
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
index 7113ee5b68..f2b1e092b7 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
@@ -1026,9 +1026,9 @@ public class PartitionReplicaListener implements
ReplicaListener {
.thenAccept(validationResult -> {
if (!validationResult.isSuccessful()) {
throw new IncompatibleSchemaException(String.format(
- "Operation failed because it tried to access a
row with newer schema version than transaction's [table=%d, "
+ "Operation failed because it tried to access a
row with newer schema version than transaction's [table=%s, "
+ "txSchemaVersion=%d,
rowSchemaVersion=%d]",
- validationResult.failedTableId(),
validationResult.fromSchemaVersion(), validationResult.toSchemaVersion()
+ validationResult.failedTableName(),
validationResult.fromSchemaVersion(), validationResult.toSchemaVersion()
));
}
});
@@ -1620,17 +1620,20 @@ public class PartitionReplicaListener implements
ReplicaListener {
private static void
throwIfSchemaValidationOnCommitFailed(CompatValidationResult validationResult,
TransactionResult txResult) {
if (!validationResult.isSuccessful()) {
if (validationResult.isTableDropped()) {
- // TODO: IGNITE-20966 - improve error message.
throw new MismatchingTransactionOutcomeException(
- format("Commit failed because a table was already
dropped [tableId={}]", validationResult.failedTableId()),
+ format("Commit failed because a table was already
dropped [table={}]", validationResult.failedTableName()),
txResult
);
} else {
- // TODO: IGNITE-20966 - improve error message.
throw new MismatchingTransactionOutcomeException(
- "Commit failed because schema "
- + validationResult.fromSchemaVersion() + " is
not forward-compatible with "
- + validationResult.toSchemaVersion() + " for
table " + validationResult.failedTableId(),
+ format(
+ "Commit failed because schema is not
forward-compatible "
+ + "[fromSchemaVersion={},
toSchemaVersion={}, table={}, details={}]",
+ validationResult.fromSchemaVersion(),
+ validationResult.toSchemaVersion(),
+ validationResult.failedTableName(),
+ validationResult.details()
+ ),
txResult
);
}
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidator.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidator.java
index c90a8239e7..303a2b8d2a 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidator.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidator.java
@@ -37,6 +37,7 @@ import
org.apache.ignite.internal.table.distributed.schema.SchemaSyncService;
import org.apache.ignite.internal.table.distributed.schema.TableDefinitionDiff;
import
org.apache.ignite.internal.table.distributed.schema.ValidationSchemasSource;
import org.apache.ignite.internal.tx.TransactionIds;
+import org.jetbrains.annotations.Nullable;
/**
* Validates schema compatibility.
@@ -116,7 +117,7 @@ class SchemaCompatibilityValidator {
CatalogTableDescriptor tableAtTxStart =
catalogService.table(tableId, beginTimestamp.longValue());
assert tableAtTxStart != null : "No table " + tableId + " at ts "
+ beginTimestamp;
- return CompatValidationResult.tableDropped(tableId,
tableAtTxStart.schemaId());
+ return CompatValidationResult.tableDropped(tableAtTxStart.name(),
tableAtTxStart.schemaId());
}
return validateForwardSchemaCompatibility(beginTimestamp,
commitTimestamp, tableId);
@@ -144,15 +145,23 @@ class SchemaCompatibilityValidator {
for (int i = 0; i < tableSchemas.size() - 1; i++) {
FullTableSchema oldSchema = tableSchemas.get(i);
FullTableSchema newSchema = tableSchemas.get(i + 1);
- if (!isForwardCompatible(oldSchema, newSchema)) {
- return CompatValidationResult.incompatibleChange(tableId,
oldSchema.schemaVersion(), newSchema.schemaVersion());
+
+ ValidationResult validationResult =
validateForwardSchemaCompatibility(oldSchema, newSchema);
+
+ if (validationResult.verdict == ValidatorVerdict.INCOMPATIBLE) {
+ return CompatValidationResult.incompatibleChange(
+ oldSchema.tableName(),
+ oldSchema.schemaVersion(),
+ newSchema.schemaVersion(),
+ validationResult.details()
+ );
}
}
return CompatValidationResult.success();
}
- private boolean isForwardCompatible(FullTableSchema prevSchema,
FullTableSchema nextSchema) {
+ private ValidationResult
validateForwardSchemaCompatibility(FullTableSchema prevSchema, FullTableSchema
nextSchema) {
TableDefinitionDiff diff = diffCache.computeIfAbsent(
new TableDefinitionDiffKey(prevSchema.tableId(),
prevSchema.schemaVersion(), nextSchema.schemaVersion()),
key -> nextSchema.diffFrom(prevSchema)
@@ -161,21 +170,23 @@ class SchemaCompatibilityValidator {
boolean accepted = false;
for (ForwardCompatibilityValidator validator :
FORWARD_COMPATIBILITY_VALIDATORS) {
- switch (validator.compatible(diff)) {
+ ValidationResult validationResult = validator.compatible(diff);
+ switch (validationResult.verdict) {
case COMPATIBLE:
accepted = true;
break;
case INCOMPATIBLE:
- return false;
+ return validationResult;
default:
break;
}
}
- assert accepted : "Table schema changed from " +
prevSchema.schemaVersion() + " and " + nextSchema.schemaVersion()
+ assert accepted : "Table schema changed from " +
prevSchema.schemaVersion()
+ + " to " + nextSchema.schemaVersion()
+ ", but no schema change validator voted for any change. Some
schema validator is missing.";
- return true;
+ return ValidationResult.COMPATIBLE;
}
/**
@@ -219,7 +230,12 @@ class SchemaCompatibilityValidator {
FullTableSchema oldSchema = tableSchemas.get(0);
FullTableSchema newSchema = tableSchemas.get(1);
- return CompatValidationResult.incompatibleChange(tableId,
oldSchema.schemaVersion(), newSchema.schemaVersion());
+ return CompatValidationResult.incompatibleChange(
+ oldSchema.tableName(),
+ oldSchema.schemaVersion(),
+ newSchema.schemaVersion(),
+ null
+ );
}
void failIfSchemaChangedAfterTxStart(UUID txId, HybridTimestamp
operationTimestamp, int tableId) {
@@ -230,28 +246,23 @@ class SchemaCompatibilityValidator {
assert tableAtBeginTs != null;
if (tableAtOpTs == null) {
- throw tableWasDroppedException(tableId);
+ throw
IncompatibleSchemaException.tableDropped(tableAtBeginTs.name());
}
if (tableAtOpTs.tableVersion() != tableAtBeginTs.tableVersion()) {
- throw new IncompatibleSchemaException(
- String.format(
- "Table schema was updated after the transaction
was started [table=%d, startSchema=%d, operationSchema=%d]",
- tableId, tableAtBeginTs.tableVersion(),
tableAtOpTs.tableVersion()
- )
+ throw IncompatibleSchemaException.schemaChanged(
+ tableAtBeginTs.name(),
+ tableAtBeginTs.tableVersion(),
+ tableAtOpTs.tableVersion()
);
}
}
- private static IncompatibleSchemaException tableWasDroppedException(int
tableId) {
- return new IncompatibleSchemaException(String.format("Table was
dropped [table=%d]", tableId));
- }
-
void failIfTableDoesNotExistAt(HybridTimestamp operationTimestamp, int
tableId) {
CatalogTableDescriptor tableAtOpTs = catalogService.table(tableId,
operationTimestamp.longValue());
if (tableAtOpTs == null) {
- throw tableWasDroppedException(tableId);
+ throw IncompatibleSchemaException.tableDropped(tableId);
}
}
@@ -274,6 +285,27 @@ class SchemaCompatibilityValidator {
}
}
+ private static class ValidationResult {
+ private static final ValidationResult COMPATIBLE = new
ValidationResult(ValidatorVerdict.COMPATIBLE, null);
+ private static final ValidationResult DONT_CARE = new
ValidationResult(ValidatorVerdict.DONT_CARE, null);
+
+ private final ValidatorVerdict verdict;
+ private final String details;
+
+ ValidationResult(ValidatorVerdict verdict, @Nullable String details) {
+ this.verdict = verdict;
+ this.details = details;
+ }
+
+ ValidatorVerdict verdict() {
+ return verdict;
+ }
+
+ @Nullable String details() {
+ return details;
+ }
+ }
+
private enum ValidatorVerdict {
/**
* Validator accepts a change: it's compatible.
@@ -291,47 +323,58 @@ class SchemaCompatibilityValidator {
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
private interface ForwardCompatibilityValidator {
- ValidatorVerdict compatible(TableDefinitionDiff diff);
+ ValidationResult compatible(TableDefinitionDiff diff);
}
private static class RenameTableValidator implements
ForwardCompatibilityValidator {
+ private static final ValidationResult INCOMPATIBLE = new
ValidationResult(
+ ValidatorVerdict.INCOMPATIBLE,
+ "Name of the table has been changed"
+ );
+
@Override
- public ValidatorVerdict compatible(TableDefinitionDiff diff) {
- return diff.nameDiffers() ? ValidatorVerdict.INCOMPATIBLE :
ValidatorVerdict.DONT_CARE;
+ public ValidationResult compatible(TableDefinitionDiff diff) {
+ return diff.nameDiffers() ? INCOMPATIBLE :
ValidationResult.DONT_CARE;
}
}
private static class AddColumnsValidator implements
ForwardCompatibilityValidator {
+
@Override
- public ValidatorVerdict compatible(TableDefinitionDiff diff) {
+ public ValidationResult compatible(TableDefinitionDiff diff) {
if (diff.addedColumns().isEmpty()) {
- return ValidatorVerdict.DONT_CARE;
+ return ValidationResult.DONT_CARE;
}
for (CatalogTableColumnDescriptor column : diff.addedColumns()) {
if (!column.nullable() && column.defaultValue() == null) {
- return ValidatorVerdict.INCOMPATIBLE;
+ return new ValidationResult(ValidatorVerdict.INCOMPATIBLE,
"Not null column added without default value");
}
}
- return ValidatorVerdict.COMPATIBLE;
+ return ValidationResult.COMPATIBLE;
}
}
private static class DropColumnsValidator implements
ForwardCompatibilityValidator {
+ private static final ValidationResult INCOMPATIBLE = new
ValidationResult(
+ ValidatorVerdict.INCOMPATIBLE,
+ "Columns were dropped"
+ );
+
@Override
- public ValidatorVerdict compatible(TableDefinitionDiff diff) {
- return diff.removedColumns().isEmpty() ?
ValidatorVerdict.DONT_CARE : ValidatorVerdict.INCOMPATIBLE;
+ public ValidationResult compatible(TableDefinitionDiff diff) {
+ return diff.removedColumns().isEmpty() ?
ValidationResult.DONT_CARE : INCOMPATIBLE;
}
}
@SuppressWarnings("InterfaceMayBeAnnotatedFunctional")
private interface ColumnChangeCompatibilityValidator {
- ValidatorVerdict compatible(ColumnDefinitionDiff diff);
+ ValidationResult compatible(ColumnDefinitionDiff diff);
}
private static class ChangeColumnsValidator implements
ForwardCompatibilityValidator {
- private final List<ColumnChangeCompatibilityValidator> validators =
List.of(
+ private static final List<ColumnChangeCompatibilityValidator>
validators = List.of(
// TODO: https://issues.apache.org/jira/browse/IGNITE-20948 -
add validator that says that column rename is compatible.
new ChangeNullabilityValidator(),
new ChangeDefaultValueValidator(),
@@ -339,81 +382,89 @@ class SchemaCompatibilityValidator {
);
@Override
- public ValidatorVerdict compatible(TableDefinitionDiff diff) {
+ public ValidationResult compatible(TableDefinitionDiff diff) {
if (diff.changedColumns().isEmpty()) {
- return ValidatorVerdict.DONT_CARE;
+ return ValidationResult.DONT_CARE;
}
boolean accepted = false;
for (ColumnDefinitionDiff columnDiff : diff.changedColumns()) {
- switch (compatible(columnDiff)) {
+ ValidationResult validationResult = compatible(columnDiff);
+ switch (validationResult.verdict()) {
case COMPATIBLE:
accepted = true;
break;
case INCOMPATIBLE:
- return ValidatorVerdict.INCOMPATIBLE;
+ return validationResult;
default:
break;
}
}
- assert accepted : "Table schema changed from " +
diff.oldSchemaVersion() + " and " + diff.newSchemaVersion()
- + ", but no column change validator voted for any change.
Some schema validator is missing.";
+ assert accepted : "Table schema changed from " +
diff.oldSchemaVersion() + " to "
+ + diff.newSchemaVersion() + ", but no column change
validator voted for any change. Some schema validator is missing.";
- return ValidatorVerdict.COMPATIBLE;
+ return ValidationResult.COMPATIBLE;
}
- private ValidatorVerdict compatible(ColumnDefinitionDiff columnDiff) {
+ private static ValidationResult compatible(ColumnDefinitionDiff
columnDiff) {
boolean accepted = false;
for (ColumnChangeCompatibilityValidator validator : validators) {
- switch (validator.compatible(columnDiff)) {
+ ValidationResult validationResult =
validator.compatible(columnDiff);
+
+ switch (validationResult.verdict()) {
case COMPATIBLE:
accepted = true;
break;
case INCOMPATIBLE:
- return ValidatorVerdict.INCOMPATIBLE;
+ return validationResult;
default:
break;
}
}
- return accepted ? ValidatorVerdict.COMPATIBLE :
ValidatorVerdict.DONT_CARE;
+ return accepted ? ValidationResult.COMPATIBLE :
ValidationResult.DONT_CARE;
}
}
private static class ChangeDefaultValueValidator implements
ColumnChangeCompatibilityValidator {
@Override
- public ValidatorVerdict compatible(ColumnDefinitionDiff diff) {
- return diff.defaultChanged() ? ValidatorVerdict.INCOMPATIBLE :
ValidatorVerdict.DONT_CARE;
+ public ValidationResult compatible(ColumnDefinitionDiff diff) {
+ return diff.defaultChanged()
+ ? new ValidationResult(ValidatorVerdict.INCOMPATIBLE,
"Column default value changed")
+ : ValidationResult.DONT_CARE;
}
}
private static class ChangeNullabilityValidator implements
ColumnChangeCompatibilityValidator {
@Override
- public ValidatorVerdict compatible(ColumnDefinitionDiff diff) {
+ public ValidationResult compatible(ColumnDefinitionDiff diff) {
if (diff.notNullAdded()) {
- return ValidatorVerdict.INCOMPATIBLE;
+ return new ValidationResult(ValidatorVerdict.INCOMPATIBLE,
"Not null added");
}
+
if (diff.notNullDropped()) {
- return ValidatorVerdict.COMPATIBLE;
+ return ValidationResult.COMPATIBLE;
}
assert !diff.nullabilityChanged() : diff;
- return ValidatorVerdict.DONT_CARE;
+ return ValidationResult.DONT_CARE;
}
}
private static class ChangeColumnTypeValidator implements
ColumnChangeCompatibilityValidator {
@Override
- public ValidatorVerdict compatible(ColumnDefinitionDiff diff) {
+ public ValidationResult compatible(ColumnDefinitionDiff diff) {
if (!diff.typeChanged()) {
- return ValidatorVerdict.DONT_CARE;
+ return ValidationResult.DONT_CARE;
}
- return diff.typeChangeIsSupported() ? ValidatorVerdict.COMPATIBLE
: ValidatorVerdict.INCOMPATIBLE;
+ return diff.typeChangeIsSupported()
+ ? ValidationResult.COMPATIBLE
+ : new ValidationResult(ValidatorVerdict.INCOMPATIBLE,
"Column type changed incompatibly");
}
}
}
diff --git
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/ColumnDefinitionDiff.java
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/ColumnDefinitionDiff.java
index 0590ad4f2e..8dbe4af988 100644
---
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/ColumnDefinitionDiff.java
+++
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/schema/ColumnDefinitionDiff.java
@@ -77,4 +77,8 @@ public class ColumnDefinitionDiff {
public boolean defaultChanged() {
return !Objects.equals(oldColumn.defaultValue(),
newColumn.defaultValue());
}
+
+ public String oldName() {
+ return oldColumn.name();
+ }
}
diff --git
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
index fec5a93771..015285ba95 100644
---
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
+++
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replication/PartitionReplicaListenerTest.java
@@ -260,6 +260,8 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
private static final int ANOTHER_TABLE_ID = 2;
private static final long ANY_ENLISTMENT_CONSISTENCY_TOKEN = 1L;
+ private static final String TABLE_NAME = "test";
+ private static final String TABLE_NAME_2 = "second_test";
private final Map<UUID, Set<RowId>> pendingRows = new
ConcurrentHashMap<>();
@@ -382,7 +384,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
private KvMarshaller<TestKey, TestValue> kvMarshallerVersion2;
private final CatalogTableDescriptor tableDescriptor = new
CatalogTableDescriptor(
- TABLE_ID, 1, 2, "table", 1,
+ TABLE_ID, 1, 2, TABLE_NAME, 1,
List.of(
new CatalogTableColumnDescriptor("intKey",
ColumnType.INT32, false, 0, 0, 0, null),
new CatalogTableColumnDescriptor("strKey",
ColumnType.STRING, false, 0, 0, 0, null),
@@ -1712,7 +1714,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
}
private static FullTableSchema tableSchema(int schemaVersion,
List<CatalogTableColumnDescriptor> columns) {
- return new FullTableSchema(schemaVersion, TABLE_ID, "test", columns);
+ return new FullTableSchema(schemaVersion, TABLE_ID, TABLE_NAME,
columns);
}
private AtomicReference<Boolean> interceptFinishTxCommand() {
@@ -1777,7 +1779,8 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
MismatchingTransactionOutcomeException ex = assertWillThrowFast(future,
MismatchingTransactionOutcomeException.class);
- assertThat(ex.getMessage(), containsString("Commit failed because
schema 1 is not forward-compatible with 2"));
+ assertThat(ex.getMessage(), containsString("Commit failed because
schema is not forward-compatible [fromSchemaVersion=1, "
+ + "toSchemaVersion=2, table=test, details=Column default value
changed]"));
assertThat(committed.get(), is(false));
}
@@ -1874,7 +1877,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
IncompatibleSchemaException.class);
assertThat(ex.code(), is(Transactions.TX_INCOMPATIBLE_SCHEMA_ERR));
assertThat(ex.getMessage(), containsString(
- "Operation failed because it tried to access a row with newer
schema version than transaction's [table=1, "
+ "Operation failed because it tried to access a row with newer
schema version than transaction's [table=test, "
+ "txSchemaVersion=1, rowSchemaVersion=2]"
));
@@ -2208,7 +2211,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
assertThat(ex.code(), is(Transactions.TX_INCOMPATIBLE_SCHEMA_ERR));
assertThat(
ex.getMessage(),
- is("Table schema was updated after the transaction was
started [table=1, startSchema=1, operationSchema=2]")
+ is("Table schema was updated after the transaction was
started [table=test, startSchema=1, operationSchema=2]")
);
} else {
assertThat(future, willCompleteSuccessfully());
@@ -2218,6 +2221,9 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
private void makeSchemaChangeAfter(HybridTimestamp txBeginTs) {
CatalogTableDescriptor tableVersion1 =
mock(CatalogTableDescriptor.class);
CatalogTableDescriptor tableVersion2 =
mock(CatalogTableDescriptor.class);
+
+ when(tableVersion1.name()).thenReturn(TABLE_NAME);
+ when(tableVersion2.name()).thenReturn(TABLE_NAME_2);
when(tableVersion1.tableVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
when(tableVersion2.tableVersion()).thenReturn(NEXT_SCHEMA_VERSION);
@@ -2323,7 +2329,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
IncompatibleSchemaException ex = assertWillThrowFast(future,
IncompatibleSchemaException.class);
assertThat(ex.code(), is(Transactions.TX_INCOMPATIBLE_SCHEMA_ERR));
- assertThat(ex.getMessage(), is("Table was dropped [table=1]"));
+ assertThat(ex.getMessage(), is("Table was dropped [tableId=1]"));
}
private void makeTableBeDroppedAfter(HybridTimestamp txBeginTs) {
@@ -2333,6 +2339,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
private void makeTableBeDroppedAfter(HybridTimestamp txBeginTs, int
tableId) {
CatalogTableDescriptor tableVersion1 =
mock(CatalogTableDescriptor.class);
when(tableVersion1.tableVersion()).thenReturn(CURRENT_SCHEMA_VERSION);
+ when(tableVersion1.name()).thenReturn(TABLE_NAME);
when(catalogService.table(tableId,
txBeginTs.longValue())).thenReturn(tableVersion1);
when(catalogService.table(eq(tableId),
gt(txBeginTs.longValue()))).thenReturn(null);
@@ -2420,7 +2427,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
IncompatibleSchemaException ex = assertWillThrowFast(future,
IncompatibleSchemaException.class);
assertThat(ex.code(), is(Transactions.TX_INCOMPATIBLE_SCHEMA_ERR));
- assertThat(ex.getMessage(), is("Table was dropped [table=1]"));
+ assertThat(ex.getMessage(), is("Table was dropped [tableId=1]"));
}
@CartesianTest
@@ -2473,6 +2480,8 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
UUID txId = newTxId();
HybridTimestamp txBeginTs = beginTimestamp(txId);
+ String tableNameToBeDropped = catalogService.table(tableToBeDroppedId,
txBeginTs.longValue()).name();
+
makeTableBeDroppedAfter(txBeginTs, tableToBeDroppedId);
CompletableFuture<?> future = partitionReplicaListener.invoke(
@@ -2489,7 +2498,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
MismatchingTransactionOutcomeException ex =
assertWillThrowFast(future, MismatchingTransactionOutcomeException.class);
- assertThat(ex.getMessage(), is("Commit failed because a table was
already dropped [tableId=" + tableToBeDroppedId + "]"));
+ assertThat(ex.getMessage(), is("Commit failed because a table was
already dropped [table=" + tableNameToBeDropped + "]"));
assertThat("The transaction must have been aborted", committed.get(),
is(false));
}
@@ -2529,6 +2538,7 @@ public class PartitionReplicaListenerTest extends
IgniteAbstractTest {
private void makeSchemaBeNextVersion() {
CatalogTableDescriptor tableVersion2 =
mock(CatalogTableDescriptor.class);
when(tableVersion2.tableVersion()).thenReturn(NEXT_SCHEMA_VERSION);
+ when(tableVersion2.name()).thenReturn(TABLE_NAME_2);
when(catalogService.table(eq(TABLE_ID),
anyLong())).thenReturn(tableVersion2);
}
diff --git
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidatorTest.java
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidatorTest.java
index f9e30397e6..c7a1509697 100644
---
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidatorTest.java
+++
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/replicator/SchemaCompatibilityValidatorTest.java
@@ -69,6 +69,7 @@ import
org.apache.ignite.internal.table.distributed.schema.ValidationSchemasSour
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
import org.apache.ignite.internal.tx.TransactionIds;
import org.apache.ignite.sql.ColumnType;
+import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -148,9 +149,10 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
assertThat("Change is compatible", result.isSuccessful(), is(false));
assertThat(result.isTableDropped(), is(false));
- assertThat(result.failedTableId(), is(TABLE_ID));
+ assertThat(result.failedTableName(), is(TABLE_NAME));
assertThat(result.fromSchemaVersion(), is(1));
assertThat(result.toSchemaVersion(), is(2));
+ assertThat(result.details(), is(changeSource.expectedDetails()));
}
@ParameterizedTest
@@ -300,7 +302,7 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
@Test
void combinationOfForwardCompatibleChangesIsCompatible() {
- assertForwardCompatibleChangeAllowsCommitting(() -> List.of(
+
assertForwardCompatibleChangeAllowsCommitting((CompatibleSchemaChangeSource) ()
-> List.of(
tableSchema(1, List.of(column(INT32, false))),
// Type is widened, NOT NULL dropped.
tableSchema(2, List.of(column(INT64, true)))
@@ -321,11 +323,21 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
@Test
void oneForwardIncompatibleChangeMakesCombinationIncompatible() {
- assertForwardIncompatibleChangeDisallowsCommitting(() -> List.of(
- tableSchema(1, List.of(column(INT32, true))),
- // Type is widened (compatible), but NOT NULL added
(incompatible).
- tableSchema(2, List.of(column(INT64, false)))
- ));
+ assertForwardIncompatibleChangeDisallowsCommitting(new
SchemaChangeSource() {
+ @Override
+ public List<FullTableSchema> schemaVersions() {
+ return List.of(
+ tableSchema(1, List.of(column(INT32, true))),
+ // Type is widened (compatible), but NOT NULL added
(incompatible).
+ tableSchema(2, List.of(column(INT64, false)))
+ );
+ }
+
+ @Override
+ public String expectedDetails() {
+ return "Not null added";
+ }
+ });
}
@Test
@@ -340,7 +352,7 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
@Test
void changeOppositeToForwardCompatibleChangeIsNotBackwardCompatible() {
- assertChangeIsNotBackwardCompatible(() -> List.of(
+ assertChangeIsNotBackwardCompatible((CompatibleSchemaChangeSource) ()
-> List.of(
tableSchema(1, List.of(
intColumn("col1"),
intColumnWithDefault("col2", 42)
@@ -366,7 +378,7 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
assertThat("Change is compatible", result.isSuccessful(), is(false));
assertThat(result.isTableDropped(), is(false));
- assertThat(result.failedTableId(), is(TABLE_ID));
+ assertThat(result.failedTableName(), is(TABLE_NAME));
assertThat(result.fromSchemaVersion(), is(1));
assertThat(result.toSchemaVersion(), is(2));
}
@@ -383,6 +395,18 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
);
}
+ private static CatalogTableColumnDescriptor int64Column(String columnName)
{
+ return new CatalogTableColumnDescriptor(
+ columnName,
+ INT64,
+ false,
+ DEFAULT_PRECISION,
+ DEFAULT_SCALE,
+ DEFAULT_LENGTH,
+ null
+ );
+ }
+
private static CatalogTableColumnDescriptor nullableIntColumn(String
columnName) {
return new CatalogTableColumnDescriptor(columnName, INT32, true,
DEFAULT_PRECISION, DEFAULT_SCALE, DEFAULT_LENGTH, null);
}
@@ -407,12 +431,21 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
return new FullTableSchema(schemaVersion, TABLE_ID, name, columns);
}
- @FunctionalInterface
private interface SchemaChangeSource {
List<FullTableSchema> schemaVersions();
+
+ String expectedDetails();
}
- private enum ForwardCompatibleChange implements SchemaChangeSource {
+ @FunctionalInterface
+ private interface CompatibleSchemaChangeSource extends SchemaChangeSource {
+ @Override
+ default @Nullable String expectedDetails() {
+ return null;
+ }
+ }
+
+ private enum ForwardCompatibleChange implements
CompatibleSchemaChangeSource {
ADD_NULLABLE_COLUMN(List.of(
tableSchema(1, List.of(
intColumn("col1")
@@ -462,58 +495,101 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
}
private enum ForwardIncompatibleChange implements SchemaChangeSource {
- RENAME_TABLE(List.of(
- tableSchema(1, TABLE_NAME, List.of(
- intColumn("col1")
- )),
- tableSchema(2, ANOTHER_NAME, List.of(
- intColumn("col1")
- ))
- )),
- DROP_COLUMN(List.of(
- tableSchema(1, List.of(
- intColumn("col1"),
- intColumn("col2")
- )),
- tableSchema(2, List.of(
- intColumn("col1")
- ))
- )),
- ADD_DEFAULT(List.of(
- tableSchema(1, List.of(
- intColumn("col1")
- )),
- tableSchema(2, List.of(
- intColumnWithDefault("col1", 42)
- ))
- )),
- CHANGE_DEFAULT(List.of(
- tableSchema(1, List.of(
- intColumnWithDefault("col1", 1)
- )),
- tableSchema(2, List.of(
- intColumnWithDefault("col1", 2)
- ))
- )),
- DROP_DEFAULT(List.of(
- tableSchema(1, List.of(
- intColumnWithDefault("col1", 42)
- )),
- tableSchema(2, List.of(
- intColumn("col1")
- ))
- ));
+ RENAME_TABLE(
+ List.of(
+ tableSchema(1, TABLE_NAME, List.of(
+ intColumn("col1")
+ )),
+ tableSchema(2, ANOTHER_NAME, List.of(
+ intColumn("col1")
+ ))
+ ),
+ "Name of the table has been changed"
+ ),
+ DROP_COLUMN(
+ List.of(
+ tableSchema(1, List.of(
+ intColumn("col1"),
+ intColumn("col2")
+ )),
+ tableSchema(2, List.of(
+ intColumn("col1")
+ ))
+ ),
+ "Columns were dropped"
+ ),
+ ADD_DEFAULT(
+ List.of(
+ tableSchema(1, List.of(
+ intColumn("col1")
+ )),
+ tableSchema(2, List.of(
+ intColumnWithDefault("col1", 42)
+ ))
+ ),
+ "Column default value changed"
+ ),
+ CHANGE_DEFAULT(
+ List.of(
+ tableSchema(1, List.of(
+ intColumnWithDefault("col1", 1)
+ )),
+ tableSchema(2, List.of(
+ intColumnWithDefault("col1", 2)
+ ))
+ ),
+ "Column default value changed"
+ ),
+ DROP_DEFAULT(
+ List.of(
+ tableSchema(1, List.of(
+ intColumnWithDefault("col1", 42)
+ )),
+ tableSchema(2, List.of(
+ intColumn("col1")
+ ))
+ ),
+ "Column default value changed"
+ ),
+ NON_NULL_WITHOUT_DEFAULT(
+ List.of(
+ tableSchema(1, List.of(
+ )),
+ tableSchema(2, List.of(
+ intColumn("NON_NULL_WITHOUT_DEFAULT_COL")
+ ))
+ ),
+ "Not null column added without default value"
+ ),
+ TYPE_CHANGED(
+ List.of(
+ tableSchema(1, List.of(
+ int64Column("TYPE_CHANGED_COL")
+ )),
+ tableSchema(2, List.of(
+ intColumn("TYPE_CHANGED_COL")
+ ))
+ ),
+ "Column type changed incompatibly"
+ );
private final List<FullTableSchema> schemaVersions;
+ private final String expectedDetails;
- ForwardIncompatibleChange(List<FullTableSchema> schemaVersions) {
+ ForwardIncompatibleChange(List<FullTableSchema> schemaVersions, String
expectedDetails) {
this.schemaVersions = schemaVersions;
+ this.expectedDetails = expectedDetails;
}
@Override
public List<FullTableSchema> schemaVersions() {
return schemaVersions;
}
+
+ @Override
+ public String expectedDetails() {
+ return expectedDetails;
+ }
}
private static class Type {
@@ -574,6 +650,11 @@ class SchemaCompatibilityValidatorTest extends
BaseIgniteAbstractTest {
);
}
+ @Override
+ public String expectedDetails() {
+ return "Column type changed incompatibly";
+ }
+
private static CatalogTableColumnDescriptor columnFromType(Type type) {
return new CatalogTableColumnDescriptor(
"col",