This is an automated email from the ASF dual-hosted git repository.
krathbun pushed a commit to branch 2.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/2.1 by this push:
new 1ab1816e42 Fixes some createtable bugs: (#5990)
1ab1816e42 is described below
commit 1ab1816e42ec1647c107d14f4f0af0da26592b69
Author: Kevin Rathbun <[email protected]>
AuthorDate: Fri Dec 5 12:09:26 2025 -0500
Fixes some createtable bugs: (#5990)
- Fixes bugs with `--copy-config` (`-cc`) and `--no-default-iterators`
(`-ndi`) options of `createtable` Shell command:
- Changes to default iterator settings on src table are now carried
over to dest table using `-cc`
- `-ndi` option did not work. Tables were still created with
default iterator props. Now works as expected.
- Results of using `-cc` and `-ndi` in conjunction were incorrect
(e.g., copy config of src table (which has default iters) to dest table
(specify `-ndi`) would result in a table with default iters)
- Deprecated `-ndi` in favor of new `--no-default-table-props`
(`-ndtp`) which functions the same, but better describes the functionality.
- Fixes bugs with `NewTableConfiguration`
- `NewTableConfiguration` now considers conflicts with default
iterators and iterators set (either through `attachIterator()` or
`setProperties()`). Previously could set an iterator with the same priority or
name as the default iterator (`VersioningIterator`).
- Deprecated `NewTableConfiguration.withoutDefaultIterators()` in
favor of new `NewTableConfiguration.withoutDefaults()` which functions the
same, but better describes the functionality.
- New tests to verify expected functionality of createtable command with
`-cc` and `-ndi`, creating tables with same config as another table via Java
API, and `NewTableConfiguration` with iterator conflicts with default iterators
closes #5976
Co-authored-by: Keith Turner <[email protected]>
---
.../core/client/admin/NewTableConfiguration.java | 57 +++++++++++++--
.../core/client/admin/TableOperations.java | 4 +-
.../core/iteratorsImpl/IteratorConfigUtil.java | 47 ++++++++----
.../shell/commands/CreateTableCommand.java | 36 +++++++---
.../apache/accumulo/test/ConditionalWriterIT.java | 3 +-
.../org/apache/accumulo/test/NamespacesIT.java | 2 +-
.../accumulo/test/NewTableConfigurationIT.java | 83 +++++++++++++++++++++-
.../apache/accumulo/test/TableOperationsIT.java | 62 ++++++++++++++++
.../test/replication/CyclicReplicationIT.java | 4 +-
.../accumulo/test/shell/ShellCreateTableIT.java | 74 +++++++++++++++++++
10 files changed, 332 insertions(+), 40 deletions(-)
diff --git
a/core/src/main/java/org/apache/accumulo/core/client/admin/NewTableConfiguration.java
b/core/src/main/java/org/apache/accumulo/core/client/admin/NewTableConfiguration.java
index 74847a103f..d7ad0ff331 100644
---
a/core/src/main/java/org/apache/accumulo/core/client/admin/NewTableConfiguration.java
+++
b/core/src/main/java/org/apache/accumulo/core/client/admin/NewTableConfiguration.java
@@ -39,6 +39,7 @@ import org.apache.accumulo.core.client.summary.Summarizer;
import org.apache.accumulo.core.client.summary.SummarizerConfiguration;
import org.apache.accumulo.core.clientImpl.TableOperationsHelper;
import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.data.constraints.DefaultKeySizeConstraint;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.iterators.user.VersioningIterator;
import org.apache.accumulo.core.iteratorsImpl.IteratorConfigUtil;
@@ -48,6 +49,7 @@ import org.apache.accumulo.core.util.LocalityGroupUtil;
import
org.apache.accumulo.core.util.LocalityGroupUtil.LocalityGroupConfigurationError;
import org.apache.hadoop.io.Text;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSortedSet;
/**
@@ -64,7 +66,7 @@ public class NewTableConfiguration {
private static final InitialTableState DEFAULT_CREATION_MODE =
InitialTableState.ONLINE;
private InitialTableState initialTableState = DEFAULT_CREATION_MODE;
- private boolean limitVersion = true;
+ private boolean includeDefaults = true;
private Map<String,String> properties = Collections.emptyMap();
private Map<String,String> samplerProps = Collections.emptyMap();
@@ -105,10 +107,29 @@ public class NewTableConfiguration {
* the table to be created without that iterator, or any others which may
become defaults in the
* future.
*
+ * @deprecated since 2.1.4. This method prevents all default table
properties from being set. This
+ * includes the default iterator properties as well as the
default key size constraint
+ * ({@link DefaultKeySizeConstraint}). Replaced by {@link
#withoutDefaults()} to match
+ * name with functionality.
* @return this
*/
+ @Deprecated(since = "2.1.4")
public NewTableConfiguration withoutDefaultIterators() {
- this.limitVersion = false;
+ this.includeDefaults = false;
+ return this;
+ }
+
+ /**
+ * Currently, the default table properties include the default iterator
+ * ({@link VersioningIterator}) and the constraint {@link
DefaultKeySizeConstraint}. This method
+ * will cause the table to be created without this iterator or constraint,
and any others which
+ * may become defaults in the future.
+ *
+ * @since 2.1.4
+ * @return this
+ */
+ public NewTableConfiguration withoutDefaults() {
+ this.includeDefaults = false;
return this;
}
@@ -167,15 +188,39 @@ public class NewTableConfiguration {
public Map<String,String> getProperties() {
Map<String,String> propertyMap = new HashMap<>();
- if (limitVersion) {
-
propertyMap.putAll(IteratorConfigUtil.generateInitialTableProperties(limitVersion));
- }
-
propertyMap.putAll(summarizerProps);
propertyMap.putAll(samplerProps);
propertyMap.putAll(properties);
propertyMap.putAll(iteratorProps);
propertyMap.putAll(localityProps);
+
+ if (includeDefaults) {
+ var initTableProps = IteratorConfigUtil.getInitialTableProperties();
+ // check the properties for conflicts with default iterators
+ var defaultIterSettings =
IteratorConfigUtil.getInitialTableIteratorSettings();
+ // if a default prop already exists, don't want to consider that a
conflict
+ var noDefaultsPropMap = new HashMap<>(propertyMap);
+ noDefaultsPropMap.entrySet().removeIf(entry ->
initTableProps.get(entry.getKey()) != null
+ && initTableProps.get(entry.getKey()).equals(entry.getValue()));
+ defaultIterSettings.forEach((setting, scopes) -> {
+ try {
+ TableOperationsHelper.checkIteratorConflicts(noDefaultsPropMap,
setting, scopes);
+ } catch (AccumuloException e) {
+ throw new IllegalStateException(e);
+ }
+ });
+
+ // check the properties for conflicts with default properties
(non-iterator)
+ var nonIterDefaults = IteratorConfigUtil.getInitialTableProperties();
+
nonIterDefaults.keySet().removeAll(IteratorConfigUtil.getInitialTableIterators().keySet());
+ nonIterDefaults.forEach((dk, dv) -> {
+ var valInPropMap = propertyMap.get(dk);
+ Preconditions.checkState(valInPropMap == null ||
valInPropMap.equals(dv), String.format(
+ "conflict for property %s : %s (default val) != %s (set val)", dk,
dv, valInPropMap));
+ });
+
+ propertyMap.putAll(initTableProps);
+ }
return Collections.unmodifiableMap(propertyMap);
}
diff --git
a/core/src/main/java/org/apache/accumulo/core/client/admin/TableOperations.java
b/core/src/main/java/org/apache/accumulo/core/client/admin/TableOperations.java
index c0f85c1e2a..b59e9855bd 100644
---
a/core/src/main/java/org/apache/accumulo/core/client/admin/TableOperations.java
+++
b/core/src/main/java/org/apache/accumulo/core/client/admin/TableOperations.java
@@ -102,7 +102,7 @@ public interface TableOperations {
if (limitVersion) {
create(tableName);
} else {
- create(tableName, new NewTableConfiguration().withoutDefaultIterators());
+ create(tableName, new NewTableConfiguration().withoutDefaults());
}
}
@@ -124,7 +124,7 @@ public interface TableOperations {
if (versioningIter) {
create(tableName, ntc);
} else {
- create(tableName, ntc.withoutDefaultIterators());
+ create(tableName, ntc.withoutDefaults());
}
}
diff --git
a/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/IteratorConfigUtil.java
b/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/IteratorConfigUtil.java
index 5758411e04..59224deb9b 100644
---
a/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/IteratorConfigUtil.java
+++
b/core/src/main/java/org/apache/accumulo/core/iteratorsImpl/IteratorConfigUtil.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -75,28 +76,48 @@ public class IteratorConfigUtil {
}
/**
- * Generate the initial (default) properties for a table
+ * Get the initial (default) properties for a table. This includes
+ * {@link #getInitialTableIterators()} and a constraint {@link
DefaultKeySizeConstraint}
*
- * @param limitVersion include a VersioningIterator at priority 20 that
retains a single version
- * of a given K/V pair.
- * @return A map of Table properties
+ * @return A map of default Table properties
*/
- public static Map<String,String> generateInitialTableProperties(boolean
limitVersion) {
+ public static Map<String,String> getInitialTableProperties() {
+ TreeMap<String,String> props = new TreeMap<>(getInitialTableIterators());
+
+ props.put(Property.TABLE_CONSTRAINT_PREFIX + "1",
DefaultKeySizeConstraint.class.getName());
+
+ return props;
+ }
+
+ /**
+ * For all iterator scopes, includes a {@link VersioningIterator} at
priority 20 that retains a
+ * single version of a given K/V pair.
+ *
+ * @return a map of default Table iterator properties
+ * @see #getInitialTableIteratorSettings
+ */
+ public static Map<String,String> getInitialTableIterators() {
TreeMap<String,String> props = new TreeMap<>();
- if (limitVersion) {
- for (IteratorScope iterScope : IteratorScope.values()) {
- props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".vers",
- "20," + VersioningIterator.class.getName());
- props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() +
".vers.opt.maxVersions", "1");
- }
+ for (IteratorScope iterScope : IteratorScope.values()) {
+ props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".vers",
+ "20," + VersioningIterator.class.getName());
+ props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() +
".vers.opt.maxVersions", "1");
}
- props.put(Property.TABLE_CONSTRAINT_PREFIX + "1",
DefaultKeySizeConstraint.class.getName());
-
return props;
}
+ /**
+ *
+ * @return a map of the default Table iterator settings
+ * @see #getInitialTableIterators
+ */
+ public static Map<IteratorSetting,EnumSet<IteratorScope>>
getInitialTableIteratorSettings() {
+ return Map.of(new IteratorSetting(20, "vers",
VersioningIterator.class.getName(),
+ Map.of("maxVersions", "1")), EnumSet.allOf(IteratorScope.class));
+ }
+
public static List<IterInfo> parseIterConf(IteratorScope scope,
List<IterInfo> iters,
Map<String,Map<String,String>> allOptions, AccumuloConfiguration conf) {
Map<String,String> properties =
conf.getAllPropertiesWithPrefix(getProperty(scope));
diff --git
a/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java
b/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java
index a500e2d607..c730f502a6 100644
---
a/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java
+++
b/shell/src/main/java/org/apache/accumulo/shell/commands/CreateTableCommand.java
@@ -62,7 +62,9 @@ public class CreateTableCommand extends Command {
private Option createTableOptSplit;
private Option createTableOptTimeLogical;
private Option createTableOptTimeMillis;
+ @Deprecated(since = "2.1.4")
private Option createTableNoDefaultIters;
+ private Option createTableNoDefaultTableProps;
private Option createTableOptEVC;
private Option base64Opt;
private Option createTableOptFormatter;
@@ -150,29 +152,38 @@ public class CreateTableCommand extends Command {
}
// Copy configuration options if flag was set
+ Map<String,String> srcTableConfig = null;
if (cl.hasOption(createTableOptCopyConfig.getOpt())) {
String srcTable = cl.getOptionValue(createTableOptCopyConfig.getOpt());
if (cl.hasOption(createTableOptExcludeParentProps.getLongOpt())) {
// copy properties, excludes parent properties in configuration
- Map<String,String> tableProps =
+ srcTableConfig =
shellState.getAccumuloClient().tableOperations().getTableProperties(srcTable);
- tableProps.entrySet().stream()
- .filter(entry -> Property.isValidTablePropertyKey(entry.getKey()))
- .forEach(entry -> initProperties.put(entry.getKey(),
entry.getValue()));
} else {
// copy configuration (include parent properties)
- final Map<String,String> configuration =
+ srcTableConfig =
shellState.getAccumuloClient().tableOperations().getConfiguration(srcTable);
- configuration.entrySet().stream()
- .filter(entry -> Property.isValidTablePropertyKey(entry.getKey()))
- .forEach(entry -> initProperties.put(entry.getKey(),
entry.getValue()));
}
+ srcTableConfig.entrySet().stream()
+ .filter(entry -> Property.isValidTablePropertyKey(entry.getKey()))
+ .forEach(entry -> initProperties.put(entry.getKey(),
entry.getValue()));
+ // we want to copy the config exactly, specify no defaults so default
settings copied from
+ // src (above) are all that are added to dest (if any)
+ ntc = ntc.withoutDefaults();
}
// if no defaults selected, remove, even if copied from configuration or
properties
- if (cl.hasOption(createTableNoDefaultIters.getOpt())) {
- Set<String> initialProps =
IteratorConfigUtil.generateInitialTableProperties(true).keySet();
+ final boolean noDefaultIters =
cl.hasOption(createTableNoDefaultIters.getOpt());
+ if (noDefaultIters ||
cl.hasOption(createTableNoDefaultTableProps.getOpt())) {
+ if (noDefaultIters) {
+ Shell.log.warn("{} ({}) option is deprecated and will be removed in a
future version.",
+ createTableNoDefaultIters.getLongOpt(),
createTableNoDefaultIters.getOpt());
+ }
+ // handles if default props were copied over
+ Set<String> initialProps =
IteratorConfigUtil.getInitialTableProperties().keySet();
initialProps.forEach(initProperties::remove);
+ // prevents default props from being added in create table call
+ ntc = ntc.withoutDefaults();
}
// Load custom formatter if set
@@ -331,7 +342,9 @@ public class CreateTableCommand extends Command {
createTableOptTimeLogical = new Option("tl", "time-logical", false, "use
logical time");
createTableOptTimeMillis = new Option("tm", "time-millis", false, "use
time in milliseconds");
createTableNoDefaultIters = new Option("ndi", "no-default-iterators",
false,
- "prevent creation of the normal default iterator set");
+ "deprecated; use --no-default-table-props (-ndtp) instead. Prevent
creation of the normal default iterator set");
+ createTableNoDefaultTableProps = new Option("ndtp",
"no-default-table-props", false,
+ "prevent creation of the default iterator and default key size limit");
createTableOptEVC = new Option("evc", "enable-visibility-constraint",
false,
"prevent users from writing data they cannot read. When enabling this,"
+ " consider disabling bulk import and alter table.");
@@ -379,6 +392,7 @@ public class CreateTableCommand extends Command {
o.addOption(createTableOptCopyConfig);
o.addOption(createTableOptExcludeParentProps);
o.addOption(createTableNoDefaultIters);
+ o.addOption(createTableNoDefaultTableProps);
o.addOption(createTableOptEVC);
o.addOption(createTableOptFormatter);
o.addOption(createTableOptInitProp);
diff --git
a/test/src/main/java/org/apache/accumulo/test/ConditionalWriterIT.java
b/test/src/main/java/org/apache/accumulo/test/ConditionalWriterIT.java
index 4fb97d84ec..bccbc0ced2 100644
--- a/test/src/main/java/org/apache/accumulo/test/ConditionalWriterIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/ConditionalWriterIT.java
@@ -521,8 +521,7 @@ public class ConditionalWriterIT extends
SharedMiniClusterBase {
try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
String tableName = getUniqueNames(1)[0];
- client.tableOperations().create(tableName,
- new NewTableConfiguration().withoutDefaultIterators());
+ client.tableOperations().create(tableName, new
NewTableConfiguration().withoutDefaults());
try (BatchWriter bw = client.createBatchWriter(tableName)) {
diff --git a/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java
b/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java
index f990ae5927..f8ff450738 100644
--- a/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/NamespacesIT.java
@@ -554,7 +554,7 @@ public class NamespacesIT extends SharedMiniClusterBase {
public void verifyConstraintInheritance() throws Exception {
String t1 = namespace + ".1";
c.namespaceOperations().create(namespace);
- c.tableOperations().create(t1, new
NewTableConfiguration().withoutDefaultIterators());
+ c.tableOperations().create(t1, new
NewTableConfiguration().withoutDefaults());
String constraintClassName = NumericValueConstraint.class.getName();
assertFalse(
diff --git
a/test/src/main/java/org/apache/accumulo/test/NewTableConfigurationIT.java
b/test/src/main/java/org/apache/accumulo/test/NewTableConfigurationIT.java
index ae4e1a5685..db9564ff77 100644
--- a/test/src/main/java/org/apache/accumulo/test/NewTableConfigurationIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/NewTableConfigurationIT.java
@@ -20,6 +20,8 @@ package org.apache.accumulo.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
import java.util.Collections;
@@ -39,7 +41,9 @@ import org.apache.accumulo.core.client.TableExistsException;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.admin.NewTableConfiguration;
import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.data.constraints.DefaultKeySizeConstraint;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
+import org.apache.accumulo.core.iteratorsImpl.IteratorConfigUtil;
import org.apache.accumulo.harness.SharedMiniClusterBase;
import org.apache.hadoop.io.Text;
import org.junit.jupiter.api.AfterAll;
@@ -199,7 +203,7 @@ public class NewTableConfigurationIT extends
SharedMiniClusterBase {
AccumuloException, TableExistsException, TableNotFoundException {
try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
String tableName = getUniqueNames(2)[0];
- NewTableConfiguration ntc = new
NewTableConfiguration().withoutDefaultIterators();
+ NewTableConfiguration ntc = new
NewTableConfiguration().withoutDefaults();
Map<String,Set<Text>> lgroups = new HashMap<>();
lgroups.put("lg1", Set.of(new Text("colF")));
@@ -336,7 +340,7 @@ public class NewTableConfigurationIT extends
SharedMiniClusterBase {
try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
String tableName = getUniqueNames(2)[0];
- NewTableConfiguration ntc = new
NewTableConfiguration().withoutDefaultIterators();
+ NewTableConfiguration ntc = new
NewTableConfiguration().withoutDefaults();
IteratorSetting setting = new IteratorSetting(10, "myIterator",
"my.class");
ntc.attachIterator(setting);
client.tableOperations().create(tableName, ntc);
@@ -482,7 +486,7 @@ public class NewTableConfigurationIT extends
SharedMiniClusterBase {
Map<String,Set<Text>> lgroups = new HashMap<>();
lgroups.put("lgp", Set.of(new Text("col")));
- NewTableConfiguration ntc = new
NewTableConfiguration().withoutDefaultIterators()
+ NewTableConfiguration ntc = new NewTableConfiguration().withoutDefaults()
.attachIterator(setting,
EnumSet.of(IteratorScope.scan)).setLocalityGroups(lgroups);
client.tableOperations().create(tableName, ntc);
@@ -517,6 +521,79 @@ public class NewTableConfigurationIT extends
SharedMiniClusterBase {
}
}
+ @Test
+ public void testConflictsWithDefaults() throws Exception {
+ // tests trying to add properties that conflict with the default table
properties
+ String[] tableNames = getUniqueNames(3);
+ String table = tableNames[0];
+ String table2 = tableNames[1];
+ String table3 = tableNames[2];
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ /*
+ * conflicts from iterators set via attachIterator()
+ */
+ // add an iterator with same priority as the default iterator
+ var iterator1 = new IteratorSetting(20, "foo", "foo.bar");
+ var exception = assertThrows(IllegalStateException.class, () ->
client.tableOperations()
+ .create(table, new
NewTableConfiguration().attachIterator(iterator1)));
+ assertTrue(exception.getMessage().contains("iterator priority
conflict"));
+ // add an iterator with same name as the default iterator
+ var iterator2 = new IteratorSetting(10, "vers", "foo.bar");
+ exception = assertThrows(IllegalStateException.class, () ->
client.tableOperations()
+ .create(table, new
NewTableConfiguration().attachIterator(iterator2)));
+ assertTrue(exception.getMessage().contains("iterator name conflict"));
+ // try to attach the exact default iterators, should not present a
conflict as they are
+ // equivalent to what would be added
+ IteratorConfigUtil.getInitialTableIteratorSettings().forEach((setting,
scopes) -> {
+ try {
+ client.tableOperations().create(table,
+ new NewTableConfiguration().attachIterator(setting, scopes));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ /*
+ * conflicts from iterators set via properties
+ */
+ // add an iterator with same priority as the default iterator
+ Map<String,String> props = new HashMap<>();
+ for (IteratorScope iterScope : IteratorScope.values()) {
+ props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".foo",
"20,foo.bar");
+ }
+ exception = assertThrows(IllegalStateException.class, () ->
client.tableOperations()
+ .create(table2, new NewTableConfiguration().setProperties(props)));
+ assertTrue(exception.getMessage().contains("iterator priority
conflict"));
+ props.clear();
+ // add an iterator with same name as the default iterator
+ for (IteratorScope iterScope : IteratorScope.values()) {
+ props.put(Property.TABLE_ITERATOR_PREFIX + iterScope.name() + ".vers",
"10,foo.bar");
+ }
+ exception = assertThrows(IllegalStateException.class, () ->
client.tableOperations()
+ .create(table2, new NewTableConfiguration().setProperties(props)));
+ assertTrue(exception.getMessage().contains("iterator name conflict"));
+ props.clear();
+ // try to attach the exact default iterators, should not present a
conflict as they are
+ // equivalent to what would be added
+ client.tableOperations().create(table2,
+ new
NewTableConfiguration().setProperties(IteratorConfigUtil.getInitialTableIterators()));
+
+ /*
+ * conflicts with default, non-iterator properties
+ */
+ // setting a value different from default should throw
+ props.put(Property.TABLE_CONSTRAINT_PREFIX + "1", "foo");
+ exception = assertThrows(IllegalStateException.class, () ->
client.tableOperations()
+ .create(table3, new NewTableConfiguration().setProperties(props)));
+ assertTrue(exception.getMessage()
+ .contains("conflict for property " +
Property.TABLE_CONSTRAINT_PREFIX + "1"));
+ props.clear();
+ // setting a value equal to default should be fine
+ props.put(Property.TABLE_CONSTRAINT_PREFIX + "1",
DefaultKeySizeConstraint.class.getName());
+ client.tableOperations().create(table3, new
NewTableConfiguration().setProperties(props));
+ }
+ }
+
/**
* Verify the expected iterator properties exist.
*/
diff --git a/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java
b/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java
index 2ca5565eb2..b49c3eb64a 100644
--- a/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/TableOperationsIT.java
@@ -62,11 +62,13 @@ import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.PartialKey;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.constraints.DefaultKeySizeConstraint;
+import org.apache.accumulo.core.iterators.IteratorUtil;
import org.apache.accumulo.core.security.Authorizations;
import org.apache.accumulo.core.security.TablePermission;
import org.apache.accumulo.harness.AccumuloClusterHarness;
import org.apache.accumulo.test.functional.BadIterator;
import org.apache.accumulo.test.functional.FunctionalTestUtils;
+import org.apache.accumulo.test.util.Wait;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.io.Text;
import org.junit.jupiter.api.AfterEach;
@@ -454,4 +456,64 @@ public class TableOperationsIT extends
AccumuloClusterHarness {
"specified table does not exist");
}
+ @Test
+ public void testTablePropsEqual() throws Exception {
+ // Want to ensure users can somehow create a table via the Java API that
has exactly
+ // the same properties of another table (including changes to default
properties), similar to
+ // the copy config option of the create table Shell command.
+
+ // default iterator property which is automatically set on creation of all
tables; want to
+ // ensure removal and changing this type of prop is retained on copying to
another table
+ final String defaultScanIterProp = Property.TABLE_ITERATOR_PREFIX
+ + IteratorUtil.IteratorScope.scan.name().toLowerCase() + ".vers";
+ final String defaultScanIterPropMaxVers = Property.TABLE_ITERATOR_PREFIX
+ + IteratorUtil.IteratorScope.scan.name().toLowerCase() +
".vers.opt.maxVersions";
+ final String defaultScanIterPropMaxVersDefault = "1";
+ final String defaultScanIterPropMaxVersNew = "999";
+ final String customTableProp =
Property.TABLE_ARBITRARY_PROP_PREFIX.getKey() + "foo";
+ final String customTablePropVal = "bar";
+ final String[] names = getUniqueNames(3);
+ final String table1 = names[0];
+ final String table2 = names[1];
+ final String table3 = names[2];
+
+ try (AccumuloClient client =
Accumulo.newClient().from(getClientProps()).build()) {
+ client.tableOperations().create(table1,
+ new NewTableConfiguration().setProperties(Map.of(customTableProp,
customTablePropVal)));
+
+ Wait.waitFor(() -> {
+ var table1Props = client.tableOperations().getTableProperties(table1);
+ return table1Props.get(customTableProp).equals(customTablePropVal)
+ && table1Props.get(defaultScanIterProp) != null && table1Props
+
.get(defaultScanIterPropMaxVers).equals(defaultScanIterPropMaxVersDefault);
+ });
+
+ // testing both modifying and deleting a default prop
+ client.tableOperations().modifyProperties(table1, props -> {
+ props.replace(defaultScanIterPropMaxVers,
defaultScanIterPropMaxVersNew);
+ props.remove(defaultScanIterProp);
+ });
+
+ Wait.waitFor(() -> {
+ var table1Props = client.tableOperations().getTableProperties(table1);
+ return table1Props.get(customTableProp).equals(customTablePropVal)
+ && table1Props.get(defaultScanIterProp) == null
+ &&
table1Props.get(defaultScanIterPropMaxVers).equals(defaultScanIterPropMaxVersNew);
+ });
+
+ // one option to create a table with the same config, including default
prop changes
+ client.tableOperations().create(table2, new
NewTableConfiguration().withoutDefaults()
+ .setProperties(client.tableOperations().getTableProperties(table1)));
+ // another option is cloning the table
+ client.tableOperations().clone(table1, table3,
CloneConfiguration.empty());
+
+ Wait.waitFor(() -> {
+ var table1Props = client.tableOperations().getTableProperties(table1);
+ var table2Props = client.tableOperations().getTableProperties(table2);
+ var table3Props = client.tableOperations().getTableProperties(table3);
+ return table1Props.equals(table2Props) &&
table1Props.equals(table3Props);
+ });
+ }
+ }
+
}
diff --git
a/test/src/main/java/org/apache/accumulo/test/replication/CyclicReplicationIT.java
b/test/src/main/java/org/apache/accumulo/test/replication/CyclicReplicationIT.java
index 2db8808e6c..11cf52e963 100644
---
a/test/src/main/java/org/apache/accumulo/test/replication/CyclicReplicationIT.java
+++
b/test/src/main/java/org/apache/accumulo/test/replication/CyclicReplicationIT.java
@@ -263,12 +263,12 @@ public class CyclicReplicationIT extends AccumuloITBase {
manager1Cluster.getZooKeepers())));
clientManager1.tableOperations().create(manager1Table,
- new NewTableConfiguration().withoutDefaultIterators());
+ new NewTableConfiguration().withoutDefaults());
String manager1TableId =
clientManager1.tableOperations().tableIdMap().get(manager1Table);
assertNotNull(manager1TableId);
clientManager2.tableOperations().create(manager2Table,
- new NewTableConfiguration().withoutDefaultIterators());
+ new NewTableConfiguration().withoutDefaults());
String manager2TableId =
clientManager2.tableOperations().tableIdMap().get(manager2Table);
assertNotNull(manager2TableId);
diff --git
a/test/src/main/java/org/apache/accumulo/test/shell/ShellCreateTableIT.java
b/test/src/main/java/org/apache/accumulo/test/shell/ShellCreateTableIT.java
index 18309fe5d8..a723d50dea 100644
--- a/test/src/main/java/org/apache/accumulo/test/shell/ShellCreateTableIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/shell/ShellCreateTableIT.java
@@ -50,7 +50,9 @@ import org.apache.accumulo.core.client.AccumuloClient;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.TableId;
+import org.apache.accumulo.core.iterators.IteratorUtil;
import org.apache.accumulo.core.util.TextUtil;
import org.apache.accumulo.harness.MiniClusterConfigurationCallback;
import org.apache.accumulo.harness.SharedMiniClusterBase;
@@ -872,6 +874,78 @@ public class ShellCreateTableIT extends
SharedMiniClusterBase {
ts.exec("createtable --copy-config " + names[0] + " --exclude-parent " +
names[2], true);
}
+ @Test
+ public void testCreateTableCopiedConfig() throws Exception {
+ // tests that changes to default iterator settings on table1 are carried
over to a new table2
+ // when created using the config of table1
+ final String[] tableNames = getUniqueNames(2);
+ final String table1 = tableNames[0];
+ final String table2 = tableNames[1];
+
+ ts.exec("createtable " + table1, true);
+ for (IteratorUtil.IteratorScope iterScope :
IteratorUtil.IteratorScope.values()) {
+ ts.exec("config -t " + table1 + " -d " + Property.TABLE_ITERATOR_PREFIX
+ iterScope.name()
+ + ".vers", true);
+ ts.exec("config -t " + table1 + " -s " + Property.TABLE_ITERATOR_PREFIX
+ iterScope.name()
+ + ".vers.opt.maxVersions=999", true);
+ }
+
+ ts.exec("createtable " + table2 + " -cc " + table1, true);
+ for (IteratorUtil.IteratorScope iterScope :
IteratorUtil.IteratorScope.values()) {
+ var res = ts.exec(
+ "config -t " + table2 + " -f " + Property.TABLE_ITERATOR_PREFIX +
iterScope.name(), true);
+ // verify deleted table1 prop is also deleted in table2
+ // note the space: ".vers "
+ assertFalse(res.contains(Property.TABLE_ITERATOR_PREFIX +
iterScope.name() + ".vers "));
+ // verify changed table1 prop is also changed in table2
+ assertTrue(res
+ .contains(Property.TABLE_ITERATOR_PREFIX + iterScope.name() +
".vers.opt.maxVersions"));
+ assertTrue(res.contains("999"));
+ }
+ }
+
+ @Test
+ public void testCreateTableNoDefaultIterators1() throws Exception {
+ // tests the no default iterators setting
+ final String table1 = getUniqueNames(1)[0];
+
+ ts.exec("createtable -ndi " + table1, true);
+ // verify no default iterator props
+ for (IteratorUtil.IteratorScope iterScope :
IteratorUtil.IteratorScope.values()) {
+ var res = ts.exec(
+ "config -t " + table1 + " -f " + Property.TABLE_ITERATOR_PREFIX +
iterScope.name(), true);
+ assertFalse(res.contains(Property.TABLE_ITERATOR_PREFIX +
iterScope.name() + ".vers"));
+ }
+ }
+
+ @Test
+ public void testCreateTableNoDefaultIterators2() throws Exception {
+ // tests the no default iterators setting
+ final String[] tableNames = getUniqueNames(3);
+ final String table1 = tableNames[0];
+ final String table2 = tableNames[1];
+ final String table3 = tableNames[2];
+
+ // create table1 with default iterators, create table2 without default
iterators but copying
+ // table1 config... Expect no default iterators
+ ts.exec("createtable " + table1, true);
+ // make sure order doesn't matter
+ ts.exec("createtable " + table2 + " -ndi -cc " + table1);
+ ts.exec("createtable " + table3 + " -cc " + table1 + " -ndi");
+ for (IteratorUtil.IteratorScope iterScope :
IteratorUtil.IteratorScope.values()) {
+ var res = ts.exec(
+ "config -t " + table1 + " -f " + Property.TABLE_ITERATOR_PREFIX +
iterScope.name(), true);
+ // verify default iterator props for table1 but not table2 or table3
+ assertTrue(res.contains(Property.TABLE_ITERATOR_PREFIX +
iterScope.name() + ".vers"));
+ res = ts.exec(
+ "config -t " + table2 + " -f " + Property.TABLE_ITERATOR_PREFIX +
iterScope.name(), true);
+ assertFalse(res.contains(Property.TABLE_ITERATOR_PREFIX +
iterScope.name() + ".vers"));
+ res = ts.exec(
+ "config -t " + table3 + " -f " + Property.TABLE_ITERATOR_PREFIX +
iterScope.name(), true);
+ assertFalse(res.contains(Property.TABLE_ITERATOR_PREFIX +
iterScope.name() + ".vers"));
+ }
+ }
+
private Collection<Text> generateNonBinarySplits(final int numItems, final
int len) {
Set<Text> splits = new HashSet<>();
for (int i = 0; i < numItems; i++) {