diff --git a/table/src/main/java/tech/ydb/table/description/TableDescription.java b/table/src/main/java/tech/ydb/table/description/TableDescription.java index ee44912e2..fe3347378 100644 --- a/table/src/main/java/tech/ydb/table/description/TableDescription.java +++ b/table/src/main/java/tech/ydb/table/description/TableDescription.java @@ -3,6 +3,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -11,6 +12,7 @@ import javax.annotation.Nullable; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import tech.ydb.table.Session; @@ -48,6 +50,8 @@ public enum StoreType { private final TableTtl tableTtl; + private final Map attributes; + private TableDescription(Builder builder) { this.storeType = builder.storeType; this.primaryKeys = ImmutableList.copyOf(builder.primaryKeys); @@ -61,6 +65,7 @@ private TableDescription(Builder builder) { this.partitionStats = ImmutableList.copyOf(builder.partitionStats); this.tableTtl = builder.ttlSettings; this.changefeeds = builder.changefeeds; + this.attributes = ImmutableMap.copyOf(builder.attributes); } public static Builder newBuilder() { @@ -113,6 +118,10 @@ public List getChangefeeds() { return changefeeds; } + public Map getAttributes() { + return attributes; + } + /** * BUILDER */ @@ -129,6 +138,7 @@ public static class Builder { private final List partitionStats = new ArrayList<>(); private TableTtl ttlSettings = TableTtl.notSet(); private final List changefeeds = new ArrayList<>(); + private final Map attributes = new HashMap<>(); public Builder setStoreType(StoreType storeType) { this.storeType = storeType; @@ -363,6 +373,17 @@ public Builder addChangefeed(ChangefeedDescription changefeed) { return this; } + public Builder addAttribute(String name, String value) { + this.attributes.put(name, value); + return this; + } + + public Builder setAttributes(Map attrs) { + this.attributes.clear(); + this.attributes.putAll(attrs); + return this; + } + @Deprecated public Builder setTtlSettings(int ttlModeCase, String columnName, int expireAfterSeconds) { this.ttlSettings = new TableTtl(TtlMode.forCase(ttlModeCase), columnName, expireAfterSeconds); diff --git a/table/src/main/java/tech/ydb/table/impl/BaseSession.java b/table/src/main/java/tech/ydb/table/impl/BaseSession.java index 61863c78c..4493ca194 100644 --- a/table/src/main/java/tech/ydb/table/impl/BaseSession.java +++ b/table/src/main/java/tech/ydb/table/impl/BaseSession.java @@ -550,6 +550,10 @@ public CompletableFuture createTable( CommonProtos.FeatureFlag.Status.ENABLED : CommonProtos.FeatureFlag.Status.DISABLED); } + if (!description.getAttributes().isEmpty()) { + request.putAllAttributes(description.getAttributes()); + } + return rpc.createTable(request.build(), makeOptions(settings).build()); } @@ -628,6 +632,10 @@ public CompletableFuture alterTable(String path, AlterTableSettings sett .setReplaceDestination(renameIndex.isReplaceDestination()).build()); } + if (!settings.getAlterAttributes().isEmpty()) { + builder.putAllAlterAttributes(settings.getAlterAttributes()); + } + return rpc.alterTable(builder.build(), makeOptions(settings).build()); } @@ -977,6 +985,10 @@ private static Result mapDescribeTable(Result { private final List renameIndices = new ArrayList<>(); + private final Map alterAttributes = new HashMap<>(); + @Nullable private TableTtl ttl; @Nullable @@ -200,6 +202,20 @@ public TtlSettings getTtlSettings() { return new TtlSettings(ttl.getDateTimeColumn(), ttl.getExpireAfterSeconds()); } + public AlterTableSettings alterAttribute(String name, String value) { + alterAttributes.put(name, value); + return this; + } + + public AlterTableSettings dropAttribute(String name) { + alterAttributes.put(name, ""); + return this; + } + + public Map getAlterAttributes() { + return alterAttributes; + } + @Nullable public PartitioningSettings getPartitioningSettings() { return partitioningSettings; diff --git a/table/src/test/java/tech/ydb/table/integration/AlterTableTest.java b/table/src/test/java/tech/ydb/table/integration/AlterTableTest.java index b54689a12..1c476aea1 100644 --- a/table/src/test/java/tech/ydb/table/integration/AlterTableTest.java +++ b/table/src/test/java/tech/ydb/table/integration/AlterTableTest.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.After; import org.junit.Assert; @@ -283,6 +284,81 @@ public void renameIndexTest() { assertIndexSync(description.getIndexes().get(0), "idx2", Arrays.asList("id", "code"), Collections.emptyList()); } + /** + * Checks that create table with 3 custom attributes pass + */ + @Test + public void createAttributesTest() { + // --------------------- create table with attributes ----------------------------- + Map attrs = createTable(); + + Assert.assertEquals("1", attrs.get("scheme_version")); + Assert.assertEquals("test-service", attrs.get("owner")); + Assert.assertEquals("max", attrs.get("author")); + Assert.assertEquals(3, attrs.size()); + } + + /** + * Checks that test overwrites an existing attribute + * and adds a new one in a single alter request. + *

+ * The table is created with three attributes, + * then scheme_version is changed from "code 1" to "code 2" + * and a new {env attribute is added. + * After the alter, expected return four + * attributes with the untouched ones owner, author preserved. + */ + @Test + public void modifyAttributesTest() { + createTable(); + + // --------------------- alter: modify one attribute, add another ----------------------------- + Status alterStatus = ctx.supplyStatus( + session -> session.alterTable(tablePath, new AlterTableSettings() + .alterAttribute("scheme_version", "2") + .alterAttribute("env", "production")) + ).join(); + Assert.assertTrue("Alter table attributes " + alterStatus, alterStatus.isSuccess()); + + Result describeResult = ctx.supplyResult(session -> session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe after alter " + describeResult.getStatus(), describeResult.isSuccess()); + + Map attrs = describeResult.getValue().getAttributes(); + Assert.assertEquals("2", attrs.get("scheme_version")); + Assert.assertEquals("test-service", attrs.get("owner")); + Assert.assertEquals("production", attrs.get("env")); + Assert.assertEquals("max", attrs.get("author")); + Assert.assertEquals(4, attrs.size()); + } + + /** + * Checks that test removes a single attribute from the table + * and leaves the rest of them intact. + *

+ * The table is created with three attributes, then owner is dropped. + * After the alter is expected to return only scheme_version and @code author. + */ + @Test + public void dropAttributesTest() { + // --------------------- create table with attributes ----------------------------- + createTable(); + + // --------------------- alter: drop an attribute ----------------------------- + Status alterStatus = ctx.supplyStatus( + session -> session.alterTable(tablePath, new AlterTableSettings() + .dropAttribute("owner")) + ).join(); + Assert.assertTrue("Drop table attribute " + alterStatus, alterStatus.isSuccess()); + + Result describeResult = ctx.supplyResult(session -> session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe after drop " + describeResult.getStatus(), describeResult.isSuccess()); + + Map attrs = describeResult.getValue().getAttributes(); + Assert.assertEquals("1", attrs.get("scheme_version")); + Assert.assertEquals("max", attrs.get("author")); + Assert.assertEquals(2, attrs.size()); + } + private void assertColumn(TableColumn column, String name, Type type) { assertColumn(column, name, type, false, false); } @@ -315,4 +391,27 @@ private void assertListEquals(List expected, List values) { Assert.assertEquals(expected.get(idx), values.get(idx)); } } + + private Map createTable() { + // --------------------- create table with attributes ----------------------------- + TableDescription createTableDesc = TableDescription.newBuilder() + .addNonnullColumn("id", PrimitiveType.Uint64) + .addNullableColumn("value", PrimitiveType.Text) + .setPrimaryKey("id") + .addAttribute("scheme_version", "1") + .addAttribute("owner", "test-service") + .addAttribute("author", "max") + .build(); + + Status createStatus = ctx.supplyStatus( + session -> session.createTable(tablePath, createTableDesc, new CreateTableSettings()) + ).join(); + Assert.assertTrue("Create table with attributes " + createStatus, createStatus.isSuccess()); + + // --------------------- describe table: check initial attributes ----------------------------- + Result describeResult = ctx.supplyResult(session -> session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe table with attributes " + describeResult.getStatus(), describeResult.isSuccess()); + + return describeResult.getValue().getAttributes(); + } }