This is an automated email from the ASF dual-hosted git repository. apkhmv 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 c2ec9c94e7 IGNITE-21428 Add WITH params for zone (#3374) c2ec9c94e7 is described below commit c2ec9c94e7e53baa483711d0cef3dfd1188dbb34 Author: Vadim Pakhnushev <8614891+valep...@users.noreply.github.com> AuthorDate: Thu Mar 14 18:56:20 2024 +0300 IGNITE-21428 Add WITH params for zone (#3374) --- .../apache/ignite/catalog/annotations/Zone.java | 46 +++- .../catalog/definitions/ColumnDefinition.java | 5 + .../catalog/definitions/TableDefinition.java | 10 +- .../ignite/catalog/definitions/ZoneDefinition.java | 206 ++++++++++++++- .../ignite/internal/catalog/ItCatalogDslTest.java | 9 +- .../catalog/sql/CreateFromAnnotationsImpl.java | 25 +- .../catalog/sql/CreateFromDefinitionImpl.java | 24 +- .../internal/catalog/sql/CreateZoneImpl.java | 61 ++++- .../ignite/internal/catalog/sql/WithOption.java | 24 ++ .../catalog/sql/CreateFromAnnotationsTest.java | 42 ++- .../catalog/sql/CreateFromDefinitionTest.java | 10 +- .../catalog/sql/InvalidDefinitionTest.java | 291 ++++++++++++++------- 12 files changed, 617 insertions(+), 136 deletions(-) diff --git a/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java b/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java index fd9589d86c..fc8cff04c1 100644 --- a/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java +++ b/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java @@ -52,9 +52,51 @@ public @interface Zone { int replicas() default -1; /** - * Engine name. + * Affinity function. * - * @return Engine name. + * @return Affinity function. + */ + String affinityFunction() default ""; + + /** + * Timeout in seconds between node added or node left topology event itself and data nodes switch. + * + * @return Timeout. + */ + int dataNodesAutoAdjust() default -1; + + /** + * Timeout in seconds between node added topology event itself and data nodes switch. + * + * @return Timeout. + */ + int dataNodesAutoAdjustScaleUp() default -1; + + /** + * Timeout in seconds between node left topology event itself and data nodes switch. + * + * @return Timeout. + */ + int dataNodesAutoAdjustScaleDown() default -1; + + /** + * Nodes filter. + * + * @return Nodes filter. + */ + String filter() default ""; + + /** + * The storage engine name. + * + * @return The storage engine name. */ ZoneEngine engine() default ZoneEngine.DEFAULT; + + /** + * Data region name within the storage engine. + * + * @return Data region name. + */ + String dataRegion() default ""; } diff --git a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java index facebb6ff2..af32f8dd92 100644 --- a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java +++ b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java @@ -49,6 +49,7 @@ public class ColumnDefinition { if (name.isBlank()) { throw new IllegalArgumentException("Column name must not be blank."); } + Objects.requireNonNull(type, "Column type must not be null."); return new ColumnDefinition(name, type, null); } @@ -65,6 +66,10 @@ public class ColumnDefinition { if (name.isBlank()) { throw new IllegalArgumentException("Column name must not be blank."); } + Objects.requireNonNull(definition, "Column definition must not be null."); + if (definition.isBlank()) { + throw new IllegalArgumentException("Column definition must not be blank."); + } return new ColumnDefinition(name, null, definition); } diff --git a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java index 7ac92cbf60..abf9d4651e 100644 --- a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java +++ b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java @@ -247,6 +247,11 @@ public class TableDefinition { } Builder tableName(String name) { + Objects.requireNonNull(name, "Table name must not be null."); + if (name.isBlank()) { + throw new IllegalArgumentException("Table name must not be blank."); + } + this.tableName = name; return this; } @@ -495,11 +500,6 @@ public class TableDefinition { * @return Table definition. */ public TableDefinition build() { - Objects.requireNonNull(tableName, "Table name must not be null."); - if (tableName.isBlank()) { - throw new IllegalArgumentException("Table name must not be blank."); - } - return new TableDefinition( tableName, schemaName, diff --git a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java index 227e753ba6..aa4260f0c9 100644 --- a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java +++ b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java @@ -32,20 +32,44 @@ public class ZoneDefinition { private final Integer replicas; + private final String affinity; + + private final Integer dataNodesAutoAdjust; + + private final Integer dataNodesAutoAdjustScaleUp; + + private final Integer dataNodesAutoAdjustScaleDown; + + private final String filter; + private final ZoneEngine engine; + private final String dataRegion; + private ZoneDefinition( String zoneName, boolean ifNotExists, Integer partitions, Integer replicas, - ZoneEngine engine + String affinity, + Integer dataNodesAutoAdjust, + Integer dataNodesAutoAdjustScaleUp, + Integer dataNodesAutoAdjustScaleDown, + String filter, + ZoneEngine engine, + String dataRegion ) { this.zoneName = zoneName; this.ifNotExists = ifNotExists; this.partitions = partitions; this.replicas = replicas; + this.affinity = affinity; + this.dataNodesAutoAdjust = dataNodesAutoAdjust; + this.dataNodesAutoAdjustScaleUp = dataNodesAutoAdjustScaleUp; + this.dataNodesAutoAdjustScaleDown = dataNodesAutoAdjustScaleDown; + this.filter = filter; this.engine = engine; + this.dataRegion = dataRegion; } /** @@ -94,6 +118,51 @@ public class ZoneDefinition { return replicas; } + /** + * Returns affinity function. + * + * @return Affinity function. + */ + public String affinityFunction() { + return affinity; + } + + /** + * Returns timeout in seconds between node added or node left topology event itself and data nodes switch. + * + * @return Timeout. + */ + public Integer dataNodesAutoAdjust() { + return dataNodesAutoAdjust; + } + + /** + * Returns timeout in seconds between node added topology event itself and data nodes switch. + * + * @return Timeout. + */ + public Integer dataNodesAutoAdjustScaleUp() { + return dataNodesAutoAdjustScaleUp; + } + + /** + * Returns timeout in seconds between node left topology event itself and data nodes switch. + * + * @return Timeout. + */ + public Integer dataNodesAutoAdjustScaleDown() { + return dataNodesAutoAdjustScaleDown; + } + + /** + * Returns nodes filter. + * + * @return Nodes filter. + */ + public String filter() { + return filter; + } + /** * Returns the storage engine name. * @@ -103,6 +172,15 @@ public class ZoneDefinition { return engine; } + /** + * Returns the data region name within the storage engine. + * + * @return Data region name. + */ + public String dataRegion() { + return dataRegion; + } + /** * Returns new builder using this definition. * @@ -124,8 +202,20 @@ public class ZoneDefinition { private Integer replicas; + private String affinity; + + private Integer dataNodesAutoAdjust; + + private Integer dataNodesAutoAdjustScaleUp; + + private Integer dataNodesAutoAdjustScaleDown; + + private String filter; + private ZoneEngine engine = ZoneEngine.DEFAULT; + private String dataRegion; + private Builder() {} private Builder(ZoneDefinition definition) { @@ -133,7 +223,13 @@ public class ZoneDefinition { ifNotExists = definition.ifNotExists; partitions = definition.partitions; replicas = definition.replicas; + affinity = definition.affinity; + dataNodesAutoAdjust = definition.dataNodesAutoAdjust; + dataNodesAutoAdjustScaleUp = definition.dataNodesAutoAdjustScaleUp; + dataNodesAutoAdjustScaleDown = definition.dataNodesAutoAdjustScaleDown; + filter = definition.filter; engine = definition.engine; + dataRegion = definition.dataRegion; } /** @@ -143,6 +239,11 @@ public class ZoneDefinition { * @return This builder instance. */ Builder zoneName(String zoneName) { + Objects.requireNonNull(zoneName, "Zone name must not be null."); + if (zoneName.isBlank()) { + throw new IllegalArgumentException("Zone name must not be blank."); + } + this.zoneName = zoneName; return this; } @@ -183,6 +284,80 @@ public class ZoneDefinition { return this; } + /** + * Sets the affinity function. + * + * @param affinity Affinity function. + * @return This builder instance. + */ + public Builder affinity(String affinity) { + Objects.requireNonNull(affinity, "Affinity function must not be null."); + if (affinity.isBlank()) { + throw new IllegalArgumentException("Affinity function must not be blank."); + } + + this.affinity = affinity; + return this; + } + + /** + * Sets timeout in seconds between node added or node left topology event itself and data nodes switch. + * + * @param adjust Timeout. + * @return This builder instance. + */ + public Builder dataNodesAutoAdjust(Integer adjust) { + Objects.requireNonNull( + adjust, + "Timeout between node added or node left topology event itself and data nodes switch must not be null." + ); + + this.dataNodesAutoAdjust = adjust; + return this; + } + + /** + * Sets timeout in seconds between node added topology event itself and data nodes switch. + * + * @param adjust Timeout. + * @return This builder instance. + */ + public Builder dataNodesAutoAdjustScaleUp(Integer adjust) { + Objects.requireNonNull(adjust, "Timeout between node added topology event itself and data nodes switch must not be null."); + + this.dataNodesAutoAdjustScaleUp = adjust; + return this; + } + + /** + * Sets timeout in seconds between node left topology event itself and data nodes switch. + * + * @param adjust Timeout. + * @return This builder instance. + */ + public Builder dataNodesAutoAdjustScaleDown(Integer adjust) { + Objects.requireNonNull(adjust, "Timeout between node left topology event itself and data nodes switch must not be null."); + + this.dataNodesAutoAdjustScaleDown = adjust; + return this; + } + + /** + * Sets nodes filter. + * + * @param filter Nodes filter. + * @return This builder instance. + */ + public Builder filter(String filter) { + Objects.requireNonNull(filter, "Filter must not be null."); + if (filter.isBlank()) { + throw new IllegalArgumentException("Filter must not be blank."); + } + + this.filter = filter; + return this; + } + /** * Sets the storage engine name. * @@ -196,23 +371,40 @@ public class ZoneDefinition { return this; } + /** + * Sets the data region name within the storage engine. + * + * @param dataRegion Data region name within the storage engine. + * @return This builder instance. + */ + public Builder dataRegion(String dataRegion) { + Objects.requireNonNull(dataRegion, "Data region must not be null."); + if (dataRegion.isBlank()) { + throw new IllegalArgumentException("Data region must not be blank."); + } + + this.dataRegion = dataRegion; + return this; + } + /** * Builds the zone definition. * * @return Zone definition. */ public ZoneDefinition build() { - Objects.requireNonNull(zoneName, "Zone name must not be null."); - if (zoneName.isBlank()) { - throw new IllegalArgumentException("Zone name must not be blank."); - } - return new ZoneDefinition( zoneName, ifNotExists, partitions, replicas, - engine + affinity, + dataNodesAutoAdjust, + dataNodesAutoAdjustScaleUp, + dataNodesAutoAdjustScaleDown, + filter, + engine, + dataRegion ); } } diff --git a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java index 5cd8af58a8..cfd590cb42 100644 --- a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java +++ b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java @@ -29,6 +29,7 @@ import java.util.UUID; import org.apache.ignite.catalog.ColumnType; import org.apache.ignite.catalog.IgniteCatalog; import org.apache.ignite.catalog.SortOrder; +import org.apache.ignite.catalog.ZoneEngine; import org.apache.ignite.catalog.annotations.Column; import org.apache.ignite.catalog.annotations.ColumnRef; import org.apache.ignite.catalog.annotations.Id; @@ -72,7 +73,13 @@ class ItCatalogDslTest extends ClusterPerClassIntegrationTest { @Test void zoneCreateAndDropByDefinition() { // Given zone definition - ZoneDefinition zoneDefinition = ZoneDefinition.builder(ZONE_NAME).build(); + ZoneDefinition zoneDefinition = ZoneDefinition.builder(ZONE_NAME) + .affinity("affinity") + .dataNodesAutoAdjust(1) + .filter("filter") + .engine(ZoneEngine.AIMEM) + .dataRegion("dataRegion") + .build(); // When create zone from definition catalog().createZone(zoneDefinition).execute(); diff --git a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java index c0427aae8d..836d3b135a 100644 --- a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java +++ b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java @@ -111,7 +111,6 @@ class CreateFromAnnotationsImpl extends AbstractCatalogQuery { String zoneName = zone.value().isEmpty() ? zoneRef.getSimpleName() : zone.value(); createTable.zone(zoneName); createZone.name(zoneName); - createZone.engine(zone.engine()); if (zone.partitions() > 0) { createZone.partitions(zone.partitions()); } @@ -119,7 +118,29 @@ class CreateFromAnnotationsImpl extends AbstractCatalogQuery { createZone.replicas(zone.replicas()); } - // TODO https://issues.apache.org/jira/browse/IGNITE-21428 + if (!zone.affinityFunction().isEmpty()) { + createZone.affinity(zone.affinityFunction()); + } + + if (zone.dataNodesAutoAdjust() > 0) { + createZone.dataNodesAutoAdjust(zone.dataNodesAutoAdjust()); + } + if (zone.dataNodesAutoAdjustScaleUp() > 0) { + createZone.dataNodesAutoAdjustScaleUp(zone.dataNodesAutoAdjustScaleUp()); + } + if (zone.dataNodesAutoAdjustScaleDown() > 0) { + createZone.dataNodesAutoAdjustScaleDown(zone.dataNodesAutoAdjustScaleDown()); + } + + if (!zone.filter().isEmpty()) { + createZone.filter(zone.filter()); + } + + createZone.engine(zone.engine()); + + if (!zone.dataRegion().isEmpty()) { + createZone.dataRegion(zone.dataRegion()); + } } } diff --git a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java index b62035bc69..472bda3bfd 100644 --- a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java +++ b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java @@ -55,9 +55,31 @@ class CreateFromDefinitionImpl extends AbstractCatalogQuery { if (isGreaterThanZero(def.replicas())) { createZone.replicas(def.replicas()); } + + if (!StringUtils.nullOrBlank(def.affinityFunction())) { + createZone.affinity(def.affinityFunction()); + } + + if (isGreaterThanZero(def.dataNodesAutoAdjust())) { + createZone.dataNodesAutoAdjust(def.dataNodesAutoAdjust()); + } + if (isGreaterThanZero(def.dataNodesAutoAdjustScaleUp())) { + createZone.dataNodesAutoAdjustScaleUp(def.dataNodesAutoAdjustScaleUp()); + } + if (isGreaterThanZero(def.dataNodesAutoAdjustScaleDown())) { + createZone.dataNodesAutoAdjustScaleDown(def.dataNodesAutoAdjustScaleDown()); + } + + if (!StringUtils.nullOrBlank(def.filter())) { + createZone.filter(def.filter()); + } + createZone.engine(def.engine()); - // TODO https://issues.apache.org/jira/browse/IGNITE-21428 + if (!StringUtils.nullOrBlank(def.dataRegion())) { + createZone.dataRegion(def.dataRegion()); + } + return this; } diff --git a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java index 21de7464bc..1b3899a091 100644 --- a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java +++ b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java @@ -45,7 +45,7 @@ class CreateZoneImpl extends AbstractCatalogQuery { } CreateZoneImpl name(String... names) { - Objects.requireNonNull(names, "Zone name must not be null"); + Objects.requireNonNull(names, "Zone name must not be null."); this.zoneName = new Name(names); return this; @@ -56,25 +56,70 @@ class CreateZoneImpl extends AbstractCatalogQuery { return this; } - CreateZoneImpl engine(ZoneEngine engine) { - this.engine = engine; - return this; - } - CreateZoneImpl replicas(Integer n) { - Objects.requireNonNull(n, "Replicas count must not be null"); + Objects.requireNonNull(n, "Replicas count must not be null."); withOptions.add(WithOption.replicas(n)); return this; } CreateZoneImpl partitions(Integer n) { - Objects.requireNonNull(n, "Partitions must not be null"); + Objects.requireNonNull(n, "Partitions must not be null."); withOptions.add(WithOption.partitions(n)); return this; } + CreateZoneImpl affinity(String affinity) { + Objects.requireNonNull(affinity, "Affinity function must not be null."); + + withOptions.add(WithOption.affinity(affinity)); + return this; + } + + CreateZoneImpl dataNodesAutoAdjust(Integer adjust) { + Objects.requireNonNull( + adjust, + "Timeout between node added or node left topology event itself and data nodes switch must not be null." + ); + + withOptions.add(WithOption.dataNodesAutoAdjust(adjust)); + return this; + } + + CreateZoneImpl dataNodesAutoAdjustScaleUp(Integer adjust) { + Objects.requireNonNull(adjust, "Timeout between node added topology event itself and data nodes switch must not be null."); + + withOptions.add(WithOption.dataNodesAutoAdjustScaleUp(adjust)); + return this; + } + + CreateZoneImpl dataNodesAutoAdjustScaleDown(Integer adjust) { + Objects.requireNonNull(adjust, "Timeout between node left topology event itself and data nodes switch must not be null."); + + withOptions.add(WithOption.dataNodesAutoAdjustScaleDown(adjust)); + return this; + } + + CreateZoneImpl filter(String filter) { + Objects.requireNonNull(filter, "Filter must not be null."); + + withOptions.add(WithOption.filter(filter)); + return this; + } + + CreateZoneImpl engine(ZoneEngine engine) { + this.engine = engine; + return this; + } + + CreateZoneImpl dataRegion(String dataRegion) { + Objects.requireNonNull(dataRegion, "Data region must not be null."); + + withOptions.add(WithOption.dataRegion(dataRegion)); + return this; + } + @Override protected void accept(QueryContext ctx) { ctx.sql("CREATE ZONE "); diff --git a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java index b4a64811c8..d6d5355835 100644 --- a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java +++ b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java @@ -39,6 +39,30 @@ class WithOption extends QueryPart { return new WithOption("REPLICAS", replicas); } + public static WithOption affinity(String affinity) { + return new WithOption("AFFINITY_FUNCTION", affinity); + } + + public static WithOption dataNodesAutoAdjust(Integer adjust) { + return new WithOption("DATA_NODES_AUTO_ADJUST", adjust); + } + + public static WithOption dataNodesAutoAdjustScaleUp(Integer adjust) { + return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_UP", adjust); + } + + public static WithOption dataNodesAutoAdjustScaleDown(Integer adjust) { + return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_DOWN", adjust); + } + + public static WithOption filter(String filter) { + return new WithOption("DATA_NODES_FILTER", filter); + } + + public static WithOption dataRegion(String dataRegion) { + return new WithOption("DATAREGION", dataRegion); + } + @Override protected void accept(QueryContext ctx) { ctx.sql(name).sql("="); diff --git a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java index 32fa879300..cd50f1008a 100644 --- a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java +++ b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java @@ -63,9 +63,15 @@ class CreateFromAnnotationsTest { void testDefinitionCompatibility() { ZoneDefinition zoneDefinition = ZoneDefinition.builder("zone_test") .ifNotExists() - .engine(ZoneEngine.AIMEM) .partitions(1) .replicas(3) + .affinity("affinity") + .dataNodesAutoAdjust(1) + .dataNodesAutoAdjustScaleDown(2) + .dataNodesAutoAdjustScaleUp(3) + .filter("filter") + .engine(ZoneEngine.AIMEM) + .dataRegion("dataRegion") .build(); String sqlZoneFromDefinition = new CreateFromDefinitionImpl(null, Options.DEFAULT).from(zoneDefinition).toSqlString(); @@ -88,7 +94,9 @@ class CreateFromAnnotationsTest { // primitive/boxed key class is a primary key with default name 'id' assertThat( createTable().processKeyValueClasses(Integer.class, PojoValue.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS pojo_value_test (id int, f_name varchar, l_name varchar, str varchar," + " PRIMARY KEY (id)) COLOCATE BY (id, id_str) WITH PRIMARY_ZONE='ZONE_TEST';" + "CREATE INDEX IF NOT EXISTS ix_pojo ON pojo_value_test (f_name, l_name desc);") @@ -100,7 +108,9 @@ class CreateFromAnnotationsTest { // primitive/boxed key class is a primary key with default name 'id' assertThat( createTableQuoted().processKeyValueClasses(Integer.class, PojoValue.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS \"pojo_value_test\" (\"id\" int, \"f_name\" varchar, \"l_name\" varchar," + " \"str\" varchar, PRIMARY KEY (\"id\")) COLOCATE BY (\"id\", \"id_str\") WITH PRIMARY_ZONE='ZONE_TEST';" + "CREATE INDEX IF NOT EXISTS \"ix_pojo\" ON \"pojo_value_test\" (\"f_name\", \"l_name\" desc);") @@ -112,7 +122,9 @@ class CreateFromAnnotationsTest { // key class fields (annotated only) is a composite primary keys assertThat( createTable().processKeyValueClasses(PojoKey.class, PojoValue.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS pojo_value_test (id int, id_str varchar(20), f_name varchar, l_name varchar," + " str varchar, PRIMARY KEY (id, id_str)) COLOCATE BY (id, id_str) WITH PRIMARY_ZONE='ZONE_TEST';" + "CREATE INDEX IF NOT EXISTS ix_pojo ON pojo_value_test (f_name, l_name desc);") @@ -124,7 +136,9 @@ class CreateFromAnnotationsTest { // key class fields (annotated only) is a composite primary keys assertThat( createTableQuoted().processKeyValueClasses(PojoKey.class, PojoValue.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS \"pojo_value_test\" (\"id\" int, \"id_str\" varchar(20), \"f_name\" varchar," + " \"l_name\" varchar, \"str\" varchar, PRIMARY KEY (\"id\", \"id_str\")) COLOCATE BY (\"id\", \"id_str\")" + " WITH PRIMARY_ZONE='ZONE_TEST';" @@ -136,7 +150,9 @@ class CreateFromAnnotationsTest { void createFromRecordClass() { assertThat( createTable().processRecordClass(Pojo.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS pojo_test (id int, id_str varchar(20), f_name varchar(20) not null default 'a'," + " l_name varchar, str varchar, PRIMARY KEY (id, id_str))" + " COLOCATE BY (id, id_str) WITH PRIMARY_ZONE='ZONE_TEST';" @@ -148,7 +164,9 @@ class CreateFromAnnotationsTest { void createFromRecordClassQuoted() { assertThat( createTableQuoted().processRecordClass(Pojo.class).toSqlString(), - is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3;" + is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';" + "CREATE TABLE IF NOT EXISTS \"pojo_test\" (\"id\" int, \"id_str\" varchar(20)," + " \"f_name\" varchar(20) not null default 'a', \"l_name\" varchar, \"str\" varchar," + " PRIMARY KEY (\"id\", \"id_str\")) COLOCATE BY (\"id\", \"id_str\") WITH PRIMARY_ZONE='ZONE_TEST';" @@ -203,9 +221,15 @@ class CreateFromAnnotationsTest { @Zone( value = "zone_test", - replicas = 3, partitions = 1, - engine = ZoneEngine.AIMEM + replicas = 3, + affinityFunction = "affinity", + dataNodesAutoAdjust = 1, + dataNodesAutoAdjustScaleDown = 2, + dataNodesAutoAdjustScaleUp = 3, + filter = "filter", + engine = ZoneEngine.AIMEM, + dataRegion = "dataRegion" ) private static class ZoneTest {} diff --git a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java index 3d9219ff93..f0d5ee01ad 100644 --- a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java +++ b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java @@ -52,12 +52,20 @@ class CreateFromDefinitionTest { .ifNotExists() .partitions(3) .replicas(3) + .affinity("affinity") + .dataNodesAutoAdjust(1) + .dataNodesAutoAdjustScaleDown(2) + .dataNodesAutoAdjustScaleUp(3) + .filter("filter") .engine(ZoneEngine.AIMEM) + .dataRegion("dataRegion") .build(); assertThat( createZone(zone), - is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=3, REPLICAS=3;") + is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH PARTITIONS=3, REPLICAS=3, AFFINITY_FUNCTION='affinity'," + + " DATA_NODES_AUTO_ADJUST=1, DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2," + + " DATA_NODES_FILTER='filter', DATAREGION='dataRegion';") ); } diff --git a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java index 26e7222fa7..b6e0b3b443 100644 --- a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java +++ b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java @@ -17,184 +17,275 @@ package org.apache.ignite.internal.catalog.sql; +import static java.util.Collections.singletonList; +import static org.apache.ignite.catalog.ColumnType.INTEGER; +import static org.apache.ignite.catalog.IndexType.DEFAULT; import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import java.util.Collections; -import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; import org.apache.ignite.catalog.ColumnSorted; -import org.apache.ignite.catalog.ColumnType; -import org.apache.ignite.catalog.IndexType; +import org.apache.ignite.catalog.ZoneEngine; import org.apache.ignite.catalog.definitions.ColumnDefinition; import org.apache.ignite.catalog.definitions.TableDefinition; -import org.apache.ignite.catalog.definitions.TableDefinition.Builder; import org.apache.ignite.catalog.definitions.ZoneDefinition; import org.junit.jupiter.api.Test; @SuppressWarnings("ThrowableNotThrown") class InvalidDefinitionTest { @Test - void zone() { - assertThrows(NullPointerException.class, - () -> ZoneDefinition.builder(null).build(), - "Zone name must not be null."); + void zoneName() { + assertNullOrBlank(ZoneDefinition::builder, "zone", "Zone name"); + } - assertThrows(IllegalArgumentException.class, - () -> ZoneDefinition.builder("").build(), - "Zone name must not be blank."); + @Test + void zone() { + assertZoneBuilderNull(ZoneDefinition.Builder::engine, ZoneEngine.AIMEM, "Engine"); + assertZoneBuilderNull(ZoneDefinition.Builder::partitions, 1, "Number of partitions"); + assertZoneBuilderNull(ZoneDefinition.Builder::replicas, 1, "Number of replicas"); - assertThrows(NullPointerException.class, - () -> ZoneDefinition.builder("zone").engine(null).build(), - "Engine must not be null."); + assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::affinity, "a", "Affinity function"); - assertThrows(NullPointerException.class, - () -> ZoneDefinition.builder("zone").partitions(null).build(), - "Number of partitions must not be null."); + assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjust, 1, + "Timeout between node added or node left topology event itself and data nodes switch"); + assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjustScaleUp, 1, + "Timeout between node added topology event itself and data nodes switch"); + assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjustScaleDown, 1, + "Timeout between node left topology event itself and data nodes switch"); - assertThrows(NullPointerException.class, - () -> ZoneDefinition.builder("zone").replicas(null).build(), - "Number of replicas must not be null."); + assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::filter, "f", "Filter"); + assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::dataRegion, "r", "Data region"); } @Test - void names() { - assertThrows(NullPointerException.class, - () -> TableDefinition.builder(null).build(), - "Table name must not be null."); - - assertThrows(IllegalArgumentException.class, - () -> TableDefinition.builder("").build(), - "Table name must not be blank."); + void tableName() { + assertNullOrBlank(TableDefinition::builder, "table", "Table name"); } @Test void columns() { - assertThrows(NullPointerException.class, - () -> tableBuilder().columns((ColumnDefinition[]) null).build(), - "Columns array must not be null."); + ColumnDefinition validColumn = ColumnDefinition.column("id", INTEGER); - assertThrows(NullPointerException.class, - () -> tableBuilder().columns((List<ColumnDefinition>) null).build(), - "Columns list must not be null."); + assertTableBuilderNull(TableDefinition.Builder::columns, new ColumnDefinition[]{validColumn}, "Columns array"); + assertTableBuilderNull(TableDefinition.Builder::columns, singletonList(validColumn), "Columns list"); assertThrows(NullPointerException.class, - () -> tableBuilder().columns((ColumnDefinition) null).build(), + () -> tableBuilder().columns(new ColumnDefinition[]{null}), "Column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().columns(Collections.singletonList(null)).build(), + () -> tableBuilder().columns(singletonList(null)), "Column must not be null."); } @Test void colocateBy() { - assertThrows(NullPointerException.class, - () -> tableBuilder().colocateBy((String[]) null).build(), - "Colocation columns array must not be null."); - - assertThrows(NullPointerException.class, - () -> tableBuilder().colocateBy((List<String>) null).build(), - "Colocation columns list must not be null."); + assertTableBuilderNull(TableDefinition.Builder::colocateBy, new String[]{"id"}, "Colocation columns array"); + assertTableBuilderNull(TableDefinition.Builder::colocateBy, singletonList("id"), "Colocation columns list"); assertThrows(NullPointerException.class, - () -> tableBuilder().colocateBy((String) null).build(), + () -> tableBuilder().colocateBy(new String[]{null}), "Colocation column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().colocateBy(Collections.singletonList(null)).build(), + () -> tableBuilder().colocateBy(singletonList(null)), "Colocation column must not be null."); } @Test void primaryKey() { - assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey((String[]) null).build(), - "Primary key columns array must not be null."); + assertTableBuilderNull(TableDefinition.Builder::primaryKey, new String[]{"id"}, "Primary key columns array"); - assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey((String) null).build(), - "Primary key column must not be null."); + ColumnSorted validColumn = ColumnSorted.column("id"); - assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(null, (ColumnSorted[]) null).build(), - "Primary key index type must not be null."); + assertTableBuilderNull((builder, columns) -> builder.primaryKey(DEFAULT, columns), new ColumnSorted[]{validColumn}, + "Primary key columns array"); + assertTableBuilderNull((builder, columns) -> builder.primaryKey(DEFAULT, columns), singletonList(validColumn), + "Primary key columns list"); - assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(IndexType.DEFAULT, (ColumnSorted[]) null).build(), - "Primary key columns array must not be null."); + assertTableBuilderNull((builder, type) -> builder.primaryKey(type, new ColumnSorted[]{validColumn}), DEFAULT, + "Primary key index type"); + assertTableBuilderNull((builder, type) -> builder.primaryKey(type, singletonList(validColumn)), DEFAULT, + "Primary key index type"); assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(IndexType.DEFAULT, (ColumnSorted) null).build(), + () -> tableBuilder().primaryKey((String) null), "Primary key column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(null, List.of()).build(), - "Primary key index type must not be null."); - - assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(IndexType.DEFAULT, (List<ColumnSorted>) null).build(), - "Primary key columns list must not be null."); + () -> tableBuilder().primaryKey(DEFAULT, new ColumnSorted[]{null}), + "Primary key column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().primaryKey(IndexType.DEFAULT, Collections.singletonList(null)).build(), + () -> tableBuilder().primaryKey(DEFAULT, singletonList(null)), "Primary key column must not be null."); } @Test void index() { - assertThrows(NullPointerException.class, - () -> tableBuilder().index((String[]) null).build(), - "Index columns array must not be null."); + assertTableBuilderNull(TableDefinition.Builder::index, new String[]{"id"}, "Index columns array"); - assertThrows(NullPointerException.class, - () -> tableBuilder().index((String) null).build(), - "Index column must not be null."); + ColumnSorted validColumn = ColumnSorted.column("id"); - assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, null, (ColumnSorted[]) null).build(), - "Index type must not be null."); + assertTableBuilderNull((builder, columns) -> builder.index(null, DEFAULT, columns), new ColumnSorted[]{validColumn}, + "Index columns array"); + assertTableBuilderNull((builder, columns) -> builder.index(null, DEFAULT, columns), singletonList(validColumn), + "Index columns list"); - assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, IndexType.DEFAULT, (ColumnSorted[]) null).build(), - "Index columns array must not be null."); + assertTableBuilderNull((builder, type) -> builder.index(null, type, new ColumnSorted[]{validColumn}), DEFAULT, + "Index type"); + assertTableBuilderNull((builder, type) -> builder.index(null, type, singletonList(validColumn)), DEFAULT, + "Index type"); assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, IndexType.DEFAULT, (ColumnSorted) null).build(), + () -> tableBuilder().index(new String[]{null}), "Index column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, null, List.of()).build(), - "Index type must not be null."); - - assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, IndexType.DEFAULT, (List<ColumnSorted>) null).build(), - "Index columns list must not be null."); + () -> tableBuilder().index(null, DEFAULT, new ColumnSorted[]{null}), + "Index column must not be null."); assertThrows(NullPointerException.class, - () -> tableBuilder().index(null, IndexType.DEFAULT, Collections.singletonList(null)).build(), + () -> tableBuilder().index(null, DEFAULT, singletonList(null)), "Index column must not be null."); } @Test void columnDefinition() { - assertThrows(NullPointerException.class, - () -> ColumnDefinition.column(null, (ColumnType<?>) null), - "Column name must not be null."); + assertNullOrBlank(name -> ColumnDefinition.column(name, INTEGER), "column", "Column name"); + assertNullOrBlank(name -> ColumnDefinition.column(name, "definition"), "column", "Column name"); + + assertNull(type -> ColumnDefinition.column("column", type), INTEGER, "Column type"); + assertNullOrBlank(def -> ColumnDefinition.column("column", def), "definition", "Column definition"); + } - assertThrows(IllegalArgumentException.class, - () -> ColumnDefinition.column("", (ColumnType<?>) null), - "Column name must not be blank."); + private static ZoneDefinition.Builder zoneBuilder() { + return ZoneDefinition.builder("zone"); + } - assertThrows(NullPointerException.class, - () -> ColumnDefinition.column(null, (String) null), - "Column name must not be null."); + private static TableDefinition.Builder tableBuilder() { + return TableDefinition.builder("table"); + } + + /** + * Checks that the {@link ZoneDefinition.Builder} method throws {@link NullPointerException} when passed a null value and doesn't throw + * when passed a valid value. + * + * @param builderConsumer Function which takes a builder as a first argument and a tested value as a second argument. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + * @param <T> Type of the second argument. + */ + private static <T> void assertZoneBuilderNull( + BiConsumer<ZoneDefinition.Builder, T> builderConsumer, + T validValue, + String messageFragment + ) { + assertNull(InvalidDefinitionTest::zoneBuilder, builderConsumer, validValue, messageFragment); + } - assertThrows(IllegalArgumentException.class, - () -> ColumnDefinition.column("", (String) null), - "Column name must not be blank."); + /** + * Checks that the {@link ZoneDefinition.Builder} method throws {@link NullPointerException} when passed a null string, + * {@link IllegalArgumentException} when passed a blank string and doesn't throw when passed a valid value. + * + * @param builderConsumer Function which takes a builder as a first argument and a tested value as a second argument. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + */ + private static void assertZoneBuilderNullOrBlank( + BiConsumer<ZoneDefinition.Builder, String> builderConsumer, + String validValue, + String messageFragment + ) { + assertNull(InvalidDefinitionTest::zoneBuilder, builderConsumer, validValue, messageFragment); + assertBlank(InvalidDefinitionTest::zoneBuilder, builderConsumer, messageFragment); + } + /** + * Checks that the {@link TableDefinition.Builder} method throws {@link NullPointerException} when passed a null value and doesn't throw + * when passed a valid value. + * + * @param builderConsumer Function which takes a builder as a first argument and a tested value as a second argument. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + * @param <T> Type of the second argument. + */ + private static <T> void assertTableBuilderNull( + BiConsumer<TableDefinition.Builder, T> builderConsumer, + T validValue, + String messageFragment + ) { + assertNull(InvalidDefinitionTest::tableBuilder, builderConsumer, validValue, messageFragment); } - private static Builder tableBuilder() { - return TableDefinition.builder("table"); + /** + * Checks that the function throws {@link NullPointerException} when passed a null value as a second argument and doesn't throw when + * passed a valid value. + * + * @param firstArgumentSupplier Supplier of first argument. + * @param testedFunction Function to test. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + * @param <T> First argument type. + * @param <U> Second argument type. + */ + private static <T, U> void assertNull( + Supplier<T> firstArgumentSupplier, + BiConsumer<T, U> testedFunction, + U validValue, + String messageFragment + ) { + assertThrows( + NullPointerException.class, + () -> testedFunction.accept(firstArgumentSupplier.get(), null), + messageFragment + " must not be null." + ); + assertDoesNotThrow(() -> testedFunction.accept(firstArgumentSupplier.get(), validValue)); + } + + /** + * Checks that the function throws {@link NullPointerException} when passed a null value and doesn't throw when passed a valid value. + * + * @param consumer Function to test. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + */ + private static <T> void assertNull(Consumer<T> consumer, T validValue, String messageFragment) { + assertNull(() -> null, (unused, value) -> consumer.accept(value), validValue, messageFragment); + } + + /** + * Checks that the function throws {@link NullPointerException} when passed a null string, {@link IllegalArgumentException} when passed + * a blank string and doesn't throw when passed a valid value. + * + * @param consumer Function to test. + * @param validValue Valid value. + * @param messageFragment Fragment of the error text in the exception. + */ + private static void assertNullOrBlank(Consumer<String> consumer, String validValue, String messageFragment) { + assertNull(() -> null, (unused, value) -> consumer.accept(value), validValue, messageFragment); + assertBlank(() -> null, (unused, value) -> consumer.accept(value), messageFragment); + } + + /** + * Checks that the function throws {@link IllegalArgumentException} when passed a blank string as a second argument. + * + * @param firstArgumentSupplier Supplier of first argument. + * @param testedFunction Function to test. + * @param messageFragment Fragment of the error text in the exception. + * @param <T> First argument type. + */ + private static <T> void assertBlank( + Supplier<T> firstArgumentSupplier, + BiConsumer<T, String> testedFunction, + String messageFragment + ) { + assertThrows( + IllegalArgumentException.class, + () -> testedFunction.accept(firstArgumentSupplier.get(), ""), + messageFragment + " must not be blank." + ); } }