diff --git a/query/src/test/java/tech/ydb/query/TableExampleTest.java b/query/src/test/java/tech/ydb/query/TableExampleTest.java index fbbb140e..e42e2431 100644 --- a/query/src/test/java/tech/ydb/query/TableExampleTest.java +++ b/query/src/test/java/tech/ydb/query/TableExampleTest.java @@ -3,7 +3,9 @@ import java.time.Instant; import java.time.LocalDate; import java.time.Month; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -316,13 +318,29 @@ public void step07_scanQueryWithParams() { "$seasonId", PrimitiveValue.newUint64(1) ); + final List episodeTitle = new ArrayList<>(); + final List seasonTitle = new ArrayList<>(); + final List seriesTitle = new ArrayList<>(); + retryCtx.supplyStatus(session -> { + episodeTitle.clear(); + seasonTitle.clear(); + seriesTitle.clear(); + ExecuteScanQuerySettings settings = ExecuteScanQuerySettings.newBuilder().build(); GrpcReadStream scan = session.executeScanQuery(query, params, settings); return scan.start(rs -> { - Assert.assertTrue(rs.next()); + while (rs.next()) { + episodeTitle.add(rs.getColumn("episode_title").getText()); + seasonTitle.add(rs.getColumn("season_title").getText()); + seriesTitle.add(rs.getColumn("series_title").getText()); + } }); }).join().expectSuccess("scan query problem"); + + Assert.assertEquals(14, episodeTitle.size()); + Assert.assertEquals(14, seasonTitle.size()); + Assert.assertEquals(14, seriesTitle.size()); } @Test 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 c162b029..a08366d6 100644 --- a/table/src/main/java/tech/ydb/table/description/TableDescription.java +++ b/table/src/main/java/tech/ydb/table/description/TableDescription.java @@ -107,7 +107,7 @@ public static class Builder { private TableStats tableStats = null; private PartitioningSettings partitioningSettings = null; private final List partitionStats = new ArrayList<>(); - private TableTtl ttlSettings = new TableTtl(); + private TableTtl ttlSettings = TableTtl.notSet(); public Builder addNonnullColumn(String name, Type type) { return addNonnullColumn(name, type, null); @@ -222,11 +222,17 @@ public Builder addPartitionStat(long rows, long size) { return this; } + @Deprecated public Builder setTtlSettings(int ttlModeCase, String columnName, int expireAfterSeconds) { this.ttlSettings = new TableTtl(TtlMode.forCase(ttlModeCase), columnName, expireAfterSeconds); return this; } + public Builder setTtlSettings(TableTtl ttl) { + this.ttlSettings = ttl; + return this; + } + private List buildColumns() { if (columns.isEmpty()) { throw new IllegalStateException("cannot build table description with no columns"); diff --git a/table/src/main/java/tech/ydb/table/description/TableTtl.java b/table/src/main/java/tech/ydb/table/description/TableTtl.java index 14b91105..234d908b 100644 --- a/table/src/main/java/tech/ydb/table/description/TableTtl.java +++ b/table/src/main/java/tech/ydb/table/description/TableTtl.java @@ -4,23 +4,49 @@ import javax.annotation.Nullable; public class TableTtl { + private static final TableTtl NOT_SET = new TableTtl(TtlMode.NOT_SET, TtlUnit.UNSPECIFIED, "", 0, null); + @Nonnull private final TtlMode ttlMode; - @Nullable + @Nonnull + private final TtlUnit ttlUnit; + @Nonnull private final String dateTimeColumn; - @Nullable + @Nonnull private final Integer expireAfterSeconds; + @Nonnull + private final Integer runIntervalSeconds; + @Deprecated public TableTtl(@Nonnull TtlMode ttlMode, @Nonnull String dateTimeColumn, @Nonnull Integer expireAfterSeconds) { this.ttlMode = ttlMode; this.dateTimeColumn = dateTimeColumn; this.expireAfterSeconds = expireAfterSeconds; + this.ttlUnit = TtlUnit.UNSPECIFIED; + this.runIntervalSeconds = null; } + @Deprecated public TableTtl() { this.ttlMode = TtlMode.NOT_SET; - this.dateTimeColumn = null; - this.expireAfterSeconds = null; + this.dateTimeColumn = ""; + this.expireAfterSeconds = 0; + this.ttlUnit = TtlUnit.UNSPECIFIED; + this.runIntervalSeconds = null; + } + + private TableTtl( + @Nonnull TtlMode mode, + @Nonnull TtlUnit unit, + @Nonnull String columnName, + int expireAfterSeconds, + Integer runIntervalSeconds + ) { + this.ttlMode = mode; + this.dateTimeColumn = columnName; + this.expireAfterSeconds = expireAfterSeconds; + this.ttlUnit = unit; + this.runIntervalSeconds = runIntervalSeconds; } @Nonnull @@ -28,16 +54,30 @@ public TtlMode getTtlMode() { return ttlMode; } - @Nullable + @Nonnull + public TtlUnit getTtlUnit() { + return ttlUnit; + } + + @Nonnull public String getDateTimeColumn() { return dateTimeColumn; } - @Nullable + @Nonnull public Integer getExpireAfterSeconds() { return expireAfterSeconds; } + @Nullable + public Integer getRunIntervaelSeconds() { + return runIntervalSeconds; + } + + public TableTtl withRunIntervalSeconds(int seconds) { + return new TableTtl(ttlMode, ttlUnit, dateTimeColumn, expireAfterSeconds, seconds); + } + public enum TtlMode { DATE_TYPE_COLUMN(1), VALUE_SINCE_UNIX_EPOCH(2), @@ -58,4 +98,48 @@ public static TtlMode forCase(int value) { throw new IllegalArgumentException("No TTL mode defined for specified value"); } } + + public enum TtlUnit { + UNSPECIFIED, + SECONDS, + MILLISECONDS, + MICROSECONDS, + NANOSECONDS; + } + + /** + * Construct an empty TTL configuration + * + * @return instance of TTL configuration + */ + public static TableTtl notSet() { + return NOT_SET; + } + + /** + * The row will be considered as expired at the moment of time, when the value stored in columnName is less + * than or equal to the current time (in epoch time format), and expireAfterSeconds has passed since that + * moment; i.e. the expiration threshold is the value of columnNameplus expireAfterSeconds. + * + * @param columnName name of column with type Date, Datetime or Timestamp + * @param expireAfterSeconds number of seconds to add to the time in the column + * @return instance of TTL configuration + */ + public static TableTtl dateTimeColumn(@Nonnull String columnName, int expireAfterSeconds) { + return new TableTtl(TtlMode.DATE_TYPE_COLUMN, TtlUnit.UNSPECIFIED, columnName, expireAfterSeconds, null); + } + + /** + * The row will be considered as expired at the moment of time, when the value stored in columnName is less + * than or equal to the current time (in epoch time format), and expireAfterSeconds has passed since that + * moment; i.e. the expiration threshold is the value of columnNameplus expireAfterSeconds. + * + * @param columnName name of column with type UInt32, UInt64 or DyNumber + * @param unit time unit of column + * @param expireAfterSeconds number of seconds to add to the time in the column + * @return instance of TTL configuration + */ + public static TableTtl valueSinceUnixEpoch(@Nonnull String columnName, TtlUnit unit, int expireAfterSeconds) { + return new TableTtl(TtlMode.VALUE_SINCE_UNIX_EPOCH, unit, columnName, expireAfterSeconds, null); + } } 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 9e129a77..87ca9c00 100644 --- a/table/src/main/java/tech/ydb/table/impl/BaseSession.java +++ b/table/src/main/java/tech/ydb/table/impl/BaseSession.java @@ -15,6 +15,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; +import com.google.protobuf.Empty; import com.google.protobuf.Timestamp; import io.grpc.Metadata; import org.slf4j.Logger; @@ -45,6 +46,7 @@ import tech.ydb.table.description.TableColumn; import tech.ydb.table.description.TableDescription; import tech.ydb.table.description.TableIndex; +import tech.ydb.table.description.TableTtl; import tech.ydb.table.query.DataQuery; import tech.ydb.table.query.DataQueryResult; import tech.ydb.table.query.ExplainDataQueryResult; @@ -81,7 +83,6 @@ import tech.ydb.table.settings.ReplicationPolicy; import tech.ydb.table.settings.RollbackTxSettings; import tech.ydb.table.settings.StoragePolicy; -import tech.ydb.table.settings.TtlSettings; import tech.ydb.table.transaction.TableTransaction; import tech.ydb.table.transaction.Transaction; import tech.ydb.table.transaction.TxControl; @@ -268,16 +269,57 @@ private static YdbTable.ColumnFamily buildColumnFamity(ColumnFamily family) { .build(); } - private static YdbTable.TtlSettings buildTtlSettings(TtlSettings settings) { - return YdbTable.TtlSettings.newBuilder() - .setDateTypeColumn(YdbTable.DateTypeColumnModeSettings.newBuilder() - .setColumnName(settings.getDateTimeColumn()) - .setExpireAfterSeconds(settings.getExpireAfterSeconds()) - .build()) - .build(); + private static YdbTable.TtlSettings buildTtlSettings(TableTtl ttl) { + if (ttl == null || ttl.getTtlMode() == TableTtl.TtlMode.NOT_SET) { + return null; + } + + YdbTable.TtlSettings.Builder tb = YdbTable.TtlSettings.newBuilder(); + + if (ttl.getTtlMode() == TableTtl.TtlMode.DATE_TYPE_COLUMN) { + tb.setDateTypeColumn(YdbTable.DateTypeColumnModeSettings.newBuilder() + .setColumnName(ttl.getDateTimeColumn()) + .setExpireAfterSeconds(ttl.getExpireAfterSeconds()) + .build()); + } + + if (ttl.getTtlMode() == TableTtl.TtlMode.VALUE_SINCE_UNIX_EPOCH) { + YdbTable.ValueSinceUnixEpochModeSettings.Unit unit; + switch (ttl.getTtlUnit()) { + case SECONDS: + unit = YdbTable.ValueSinceUnixEpochModeSettings.Unit.UNIT_SECONDS; + break; + case MILLISECONDS: + unit = YdbTable.ValueSinceUnixEpochModeSettings.Unit.UNIT_MILLISECONDS; + break; + case MICROSECONDS: + unit = YdbTable.ValueSinceUnixEpochModeSettings.Unit.UNIT_MICROSECONDS; + break; + case NANOSECONDS: + unit = YdbTable.ValueSinceUnixEpochModeSettings.Unit.UNIT_NANOSECONDS; + break; + case UNSPECIFIED: + default: + unit = YdbTable.ValueSinceUnixEpochModeSettings.Unit.UNIT_UNSPECIFIED; + break; + } + + tb.setValueSinceUnixEpoch(YdbTable.ValueSinceUnixEpochModeSettings.newBuilder() + .setColumnName(ttl.getDateTimeColumn()) + .setColumnUnit(unit) + .setExpireAfterSeconds(ttl.getExpireAfterSeconds()) + .build()); + } + + if (ttl.getRunIntervaelSeconds() != null) { + tb.setRunIntervalSeconds(ttl.getRunIntervaelSeconds()); + } + + return tb.build(); } @Override + @SuppressWarnings("deprecation") public CompletableFuture createTable( String path, TableDescription description, @@ -301,8 +343,22 @@ public CompletableFuture createTable( request.addIndexes(buildIndex(index)); } - if (settings.getTtlSettings() != null) { - request.setTtlSettings(buildTtlSettings(settings.getTtlSettings())); + if (description.getTableTtl() != null) { + YdbTable.TtlSettings ttl = buildTtlSettings(description.getTableTtl()); + if (ttl != null) { + request.setTtlSettings(ttl); + } + } + // deprecated variant has high pripority + tech.ydb.table.settings.TtlSettings deprecatedTTL = settings.getTtlSettings(); + if (deprecatedTTL != null) { + YdbTable.TtlSettings ttl = YdbTable.TtlSettings.newBuilder() + .setDateTypeColumn(YdbTable.DateTypeColumnModeSettings.newBuilder() + .setColumnName(deprecatedTTL.getDateTimeColumn()) + .setExpireAfterSeconds(deprecatedTTL.getExpireAfterSeconds()) + .build()) + .build(); + request.setTtlSettings(ttl); } if (description.getPartitioningSettings() != null) { @@ -430,8 +486,13 @@ public CompletableFuture alterTable(String path, AlterTableSettings sett builder.addAddIndexes(buildIndex(index)); } - if (settings.getTtlSettings() != null) { - builder.setSetTtlSettings(buildTtlSettings(settings.getTtlSettings())); + if (settings.getTableTTL() != null) { + YdbTable.TtlSettings ttl = buildTtlSettings(settings.getTableTTL()); + if (ttl != null) { + builder.setSetTtlSettings(ttl); + } else { + builder.setDropTtlSettings(Empty.getDefaultInstance()); + } } if (settings.getPartitioningSettings() != null) { @@ -641,22 +702,47 @@ private static TableDescription mapDescribeTable( } } - YdbTable.TtlSettings ttlSettings = result.getTtlSettings(); - int ttlModeCase = ttlSettings.getModeCase().getNumber(); - switch (ttlSettings.getModeCase()) { + YdbTable.TtlSettings ttl = result.getTtlSettings(); + TableTtl tableTtl; + switch (ttl.getModeCase()) { case DATE_TYPE_COLUMN: - YdbTable.DateTypeColumnModeSettings dateTypeColumn = ttlSettings.getDateTypeColumn(); - description.setTtlSettings(ttlModeCase, dateTypeColumn.getColumnName(), - dateTypeColumn.getExpireAfterSeconds()); + YdbTable.DateTypeColumnModeSettings dc = ttl.getDateTypeColumn(); + tableTtl = TableTtl + .dateTimeColumn(dc.getColumnName(), dc.getExpireAfterSeconds()) + .withRunIntervalSeconds(ttl.getRunIntervalSeconds()); break; case VALUE_SINCE_UNIX_EPOCH: - YdbTable.ValueSinceUnixEpochModeSettings valueSinceUnixEpoch = ttlSettings.getValueSinceUnixEpoch(); - description.setTtlSettings(ttlModeCase, valueSinceUnixEpoch.getColumnName(), - valueSinceUnixEpoch.getExpireAfterSeconds()); + YdbTable.ValueSinceUnixEpochModeSettings vs = ttl.getValueSinceUnixEpoch(); + TableTtl.TtlUnit unit; + switch (vs.getColumnUnit()) { + case UNIT_SECONDS: + unit = TableTtl.TtlUnit.SECONDS; + break; + case UNIT_MILLISECONDS: + unit = TableTtl.TtlUnit.MILLISECONDS; + break; + case UNIT_MICROSECONDS: + unit = TableTtl.TtlUnit.MICROSECONDS; + break; + case UNIT_NANOSECONDS: + unit = TableTtl.TtlUnit.NANOSECONDS; + break; + case UNIT_UNSPECIFIED: + case UNRECOGNIZED: + default: + unit = TableTtl.TtlUnit.UNSPECIFIED; + break; + } + tableTtl = TableTtl + .valueSinceUnixEpoch(vs.getColumnName(), unit, vs.getExpireAfterSeconds()) + .withRunIntervalSeconds(ttl.getRunIntervalSeconds()); break; + case MODE_NOT_SET: default: + tableTtl = TableTtl.notSet(); break; } + description.setTtlSettings(tableTtl); return description.build(); } diff --git a/table/src/main/java/tech/ydb/table/settings/AlterTableSettings.java b/table/src/main/java/tech/ydb/table/settings/AlterTableSettings.java index 4b21452f..97ec55e5 100644 --- a/table/src/main/java/tech/ydb/table/settings/AlterTableSettings.java +++ b/table/src/main/java/tech/ydb/table/settings/AlterTableSettings.java @@ -13,6 +13,7 @@ import tech.ydb.table.description.TableColumn; import tech.ydb.table.description.TableIndex; +import tech.ydb.table.description.TableTtl; import tech.ydb.table.values.OptionalType; import tech.ydb.table.values.Type; @@ -31,7 +32,7 @@ public class AlterTableSettings extends RequestSettings { private final Set dropIndexes = new HashSet<>(); @Nullable - private TtlSettings ttlSettings; + private TableTtl ttl; @Nullable private PartitioningSettings partitioningSettings; @@ -115,8 +116,18 @@ public AlterTableSettings dropIndex(String index) { return this; } + @Deprecated public AlterTableSettings setTtlSettings(@Nullable TtlSettings ttlSettings) { - this.ttlSettings = ttlSettings; + if (ttlSettings == null) { + this.ttl = TableTtl.notSet(); + } else { + this.ttl = TableTtl.dateTimeColumn(ttlSettings.getDateTimeColumn(), ttlSettings.getExpireAfterSeconds()); + } + return this; + } + + public AlterTableSettings setTableTtl(@Nullable TableTtl ttl) { + this.ttl = ttl; return this; } @@ -150,8 +161,17 @@ public Collection getDropIndexes() { } @Nullable + public TableTtl getTableTTL() { + return ttl; + } + + @Nullable + @Deprecated public TtlSettings getTtlSettings() { - return ttlSettings; + if (ttl.getTtlMode() == TableTtl.TtlMode.NOT_SET) { + return null; + } + return new TtlSettings(ttl.getDateTimeColumn(), ttl.getExpireAfterSeconds()); } @Nullable diff --git a/table/src/main/java/tech/ydb/table/settings/CreateTableSettings.java b/table/src/main/java/tech/ydb/table/settings/CreateTableSettings.java index 4e09d936..efb15008 100644 --- a/table/src/main/java/tech/ydb/table/settings/CreateTableSettings.java +++ b/table/src/main/java/tech/ydb/table/settings/CreateTableSettings.java @@ -3,6 +3,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import tech.ydb.table.description.TableDescription; + /** * @author Sergey Polovko @@ -22,6 +24,7 @@ public class CreateTableSettings extends RequestSettings { @Nullable private ReplicationPolicy replicationPolicy; @Nullable + @SuppressWarnings("deprecation") private TtlSettings ttlSettings; @Nullable @@ -84,11 +87,21 @@ public CreateTableSettings setReplicationPolicy(@Nullable ReplicationPolicy repl return this; } + /** + * @return TTL configuration + * @deprecated use {@link TableDescription#getTableTtl() } instead + */ @Nullable + @Deprecated public TtlSettings getTtlSettings() { return ttlSettings; } + /** + * @param ttlSettings TTL configuration + * @deprecated use {@link TableDescription.Builder#setTtlSettings(tech.ydb.table.description.TableTtl)} instead + */ + @Deprecated public void setTtlSettings(@Nullable TtlSettings ttlSettings) { this.ttlSettings = ttlSettings; } diff --git a/table/src/main/java/tech/ydb/table/settings/TtlSettings.java b/table/src/main/java/tech/ydb/table/settings/TtlSettings.java index a2e4edca..c7ba794f 100644 --- a/table/src/main/java/tech/ydb/table/settings/TtlSettings.java +++ b/table/src/main/java/tech/ydb/table/settings/TtlSettings.java @@ -2,6 +2,7 @@ import javax.annotation.Nonnull; +@Deprecated public final class TtlSettings { private final String dateTimeColumn; private final int expireAfterSeconds; diff --git a/table/src/test/java/tech/ydb/table/integration/TtlTableTest.java b/table/src/test/java/tech/ydb/table/integration/TtlTableTest.java new file mode 100644 index 00000000..67388597 --- /dev/null +++ b/table/src/test/java/tech/ydb/table/integration/TtlTableTest.java @@ -0,0 +1,115 @@ +package tech.ydb.table.integration; + +import org.junit.After; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +import tech.ydb.core.Result; +import tech.ydb.core.Status; +import tech.ydb.table.SessionRetryContext; +import tech.ydb.table.description.TableDescription; +import tech.ydb.table.description.TableTtl; +import tech.ydb.table.impl.SimpleTableClient; +import tech.ydb.table.rpc.grpc.GrpcTableRpc; +import tech.ydb.table.settings.AlterTableSettings; +import tech.ydb.table.settings.CreateTableSettings; +import tech.ydb.table.values.PrimitiveType; +import tech.ydb.test.junit4.GrpcTransportRule; + +/** + * + * @author Aleksandr Gorshenin + */ +public class TtlTableTest { + @ClassRule + public final static GrpcTransportRule ydbTransport = new GrpcTransportRule(); + + private final String TABLE_NAME = "ttl_table_test"; + + private final SimpleTableClient tableClient = SimpleTableClient.newClient( + GrpcTableRpc.useTransport(ydbTransport) + ).build(); + + private final SessionRetryContext ctx = SessionRetryContext.create(tableClient).build(); + + private final String tablePath = ydbTransport.getDatabase() + "/" + TABLE_NAME; + + @After + public void dropTable() { + ctx.supplyStatus(session -> session.dropTable(tablePath)).join(); + } + + @Test + public void alterTableTest() { + // --------------------- create table ----------------------------- + TableDescription createTableDesc = TableDescription.newBuilder() + .addNonnullColumn("id", PrimitiveType.Uint64) + .addNullableColumn("date", PrimitiveType.Datetime) + .addNullableColumn("value", PrimitiveType.Uint64) + .setPrimaryKey("id") + .setTtlSettings(TableTtl.dateTimeColumn("date", 15).withRunIntervalSeconds(7200)) + .build(); + + Status createStatus = ctx.supplyStatus( + session -> session.createTable(tablePath, createTableDesc, new CreateTableSettings()) + ).join(); + Assert.assertTrue("Create table ttl " + createStatus, createStatus.isSuccess()); + + // --------------------- describe table after creating ----------------------------- + Result describeResult = ctx.supplyResult(session ->session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe table with ttl " + describeResult.getStatus(), describeResult.isSuccess()); + + TableTtl ttl = describeResult.getValue().getTableTtl(); + + Assert.assertNotNull(ttl); + Assert.assertEquals(TableTtl.TtlMode.DATE_TYPE_COLUMN, ttl.getTtlMode()); + Assert.assertEquals("date", ttl.getDateTimeColumn()); + Assert.assertEquals(Integer.valueOf(15), ttl.getExpireAfterSeconds()); + Assert.assertEquals(TableTtl.TtlUnit.UNSPECIFIED, ttl.getTtlUnit()); + Assert.assertEquals(Integer.valueOf(7200), ttl.getRunIntervaelSeconds()); + + // --------------------- alter table with changing ttl ----------------------------- + + Status alterStatus = ctx.supplyStatus( + session -> session.alterTable(tablePath, new AlterTableSettings() + .setTableTtl(TableTtl.valueSinceUnixEpoch("value", TableTtl.TtlUnit.SECONDS, 60)) + ) + ).join(); + Assert.assertTrue("Alter table with ttl " + alterStatus, alterStatus.isSuccess()); + + // --------------------- describe table after first altering ----------------------------- + describeResult = ctx.supplyResult(session ->session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe table after altering " + describeResult.getStatus(), describeResult.isSuccess()); + + ttl = describeResult.getValue().getTableTtl(); + + Assert.assertNotNull(ttl); + Assert.assertEquals(TableTtl.TtlMode.VALUE_SINCE_UNIX_EPOCH, ttl.getTtlMode()); + Assert.assertEquals("value", ttl.getDateTimeColumn()); + Assert.assertEquals(Integer.valueOf(60), ttl.getExpireAfterSeconds()); + Assert.assertEquals(TableTtl.TtlUnit.SECONDS, ttl.getTtlUnit()); + Assert.assertEquals(Integer.valueOf(0), ttl.getRunIntervaelSeconds()); + + // --------------------- alter table with dropping ttl ----------------------------- + alterStatus = ctx.supplyStatus( + session -> session.alterTable(tablePath, new AlterTableSettings() + .setTableTtl(TableTtl.notSet()) + ) + ).join(); + Assert.assertTrue("Alter table with dropping ttl " + alterStatus, alterStatus.isSuccess()); + + // --------------------- describe table after first altering ----------------------------- + describeResult = ctx.supplyResult(session ->session.describeTable(tablePath)).join(); + Assert.assertTrue("Describe table after altering " + describeResult.getStatus(), describeResult.isSuccess()); + + ttl = describeResult.getValue().getTableTtl(); + + Assert.assertNotNull(ttl); + Assert.assertEquals(TableTtl.TtlMode.NOT_SET, ttl.getTtlMode()); + Assert.assertEquals("", ttl.getDateTimeColumn()); + Assert.assertEquals(Integer.valueOf(0), ttl.getExpireAfterSeconds()); + Assert.assertEquals(TableTtl.TtlUnit.UNSPECIFIED, ttl.getTtlUnit()); + Assert.assertNull(ttl.getRunIntervaelSeconds()); + } +}