You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by sn...@apache.org on 2024/10/12 09:23:57 UTC
(flink) branch master updated: [FLINK-31380][table] Support enhanced show catalogs syntax
This is an automated email from the ASF dual-hosted git repository.
snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/master by this push:
new b9395115e63 [FLINK-31380][table] Support enhanced show catalogs syntax
b9395115e63 is described below
commit b9395115e63ce3a08a3adb940c886b8a733c5b99
Author: Ran Tao <ch...@gmail.com>
AuthorDate: Sat Oct 12 17:23:49 2024 +0800
[FLINK-31380][table] Support enhanced show catalogs syntax
---
docs/content.zh/docs/dev/table/sql/show.md | 46 +++++++++++++++++++++-
docs/content/docs/dev/table/sql/show.md | 46 +++++++++++++++++++++-
.../src/test/resources/sql/catalog_database.q | 36 +++++++++++++++++
.../src/test/resources/sql/catalog_database.q | 40 +++++++++++++++++++
.../src/main/codegen/includes/parserImpls.ftl | 31 ++++++++++++++-
.../flink/sql/parser/dql/SqlShowCatalogs.java | 31 +++++++--------
.../flink/sql/parser/FlinkSqlParserImplTest.java | 14 +++++++
.../table/operations/ShowCatalogsOperation.java | 36 ++++++++++++-----
.../operations/SqlNodeToOperationConversion.java | 9 -----
.../operations/converters/SqlNodeConverters.java | 1 +
.../converters/SqlShowCatalogsConverter.java | 38 ++++++++++++++++++
.../operations/SqlOtherOperationConverterTest.java | 30 ++++++++++++++
12 files changed, 318 insertions(+), 40 deletions(-)
diff --git a/docs/content.zh/docs/dev/table/sql/show.md b/docs/content.zh/docs/dev/table/sql/show.md
index 1b9e0460987..745c1abed79 100644
--- a/docs/content.zh/docs/dev/table/sql/show.md
+++ b/docs/content.zh/docs/dev/table/sql/show.md
@@ -548,10 +548,52 @@ Flink SQL> SHOW JARS;
## SHOW CATALOGS
```sql
-SHOW CATALOGS
+SHOW CATALOGS [ [NOT] (LIKE | ILIKE) <sql_like_pattern> ]
```
-展示所有的 catalog。
+展示所有的 catalog。另外返回的结果能被一个可选的匹配字符串过滤。
+
+**LIKE**
+根据可选的 `LIKE` 语句与 `<sql_like_pattern>` 是否模糊匹配的所有 catalog。
+
+`LIKE` 子句中 SQL 正则式的语法与 `MySQL` 方言中的语法相同。
+* `%` 匹配任意数量的字符, 也包括0数量字符, `\%` 匹配一个 `%` 字符.
+* `_` 只匹配一个字符, `\_` 匹配一个 `_` 字符.
+
+**ILIKE**
+它的行为和 LIKE 相同,只是对于大小写是不敏感的。
+
+### SHOW CATALOGS 示例
+
+假定我们在当前 flink session 中有 `catalog1` 和 `catalog2`。
+
+- 显示所有的 catalog。
+
+```sql
+show catalogs;
++-----------------+
+| catalog name |
++-----------------+
+| catalog1 |
+| catalog2 |
+| default_catalog |
++-----------------+
+3 rows in set
+```
+
+- 显示模糊匹配指定 SQL 正则式的所有catalog。
+
+```sql
+show catalogs like '%log1';
+-- show catalogs ilike '%log1';
+-- show catalogs ilike '%LOG1';
++--------------+
+| catalog name |
++--------------+
+| catalog1 |
++--------------+
+1 row in set
+```
## SHOW CURRENT CATALOG
diff --git a/docs/content/docs/dev/table/sql/show.md b/docs/content/docs/dev/table/sql/show.md
index 6ba1fa40828..9985f04577c 100644
--- a/docs/content/docs/dev/table/sql/show.md
+++ b/docs/content/docs/dev/table/sql/show.md
@@ -548,10 +548,52 @@ Flink SQL> SHOW JARS;
## SHOW CATALOGS
```sql
-SHOW CATALOGS
+SHOW CATALOGS [ [NOT] (LIKE | ILIKE) <sql_like_pattern> ]
```
-Show all catalogs.
+Show all catalogs. Additionally, the output of this statement may be filtered by an optional matching pattern.
+
+**LIKE**
+Show all catalogs with a `LIKE` clause, whose name is similar to the `<sql_like_pattern>`.
+
+The syntax of the SQL pattern in the `LIKE` clause is the same as that of the `MySQL` dialect.
+* `%` matches any number of characters, even zero characters, and `\%` matches one `%` character.
+* `_` matches exactly one character, `\_` matches one `_` character.
+
+**ILIKE**
+The same behavior as `LIKE` but the SQL pattern is case-insensitive.
+
+### SHOW CATALOGS EXAMPLES
+
+Assumes that we have `catalog1` and `catalog2` in the session.
+
+- Show all catalogs.
+
+```sql
+show catalogs;
++-----------------+
+| catalog name |
++-----------------+
+| catalog1 |
+| catalog2 |
+| default_catalog |
++-----------------+
+3 rows in set
+```
+
+- Show all catalogs, which are similar to the given SQL pattern.
+
+```sql
+show catalogs like '%log1';
+-- show catalogs ilike '%log1';
+-- show catalogs ilike '%LOG1';
++--------------+
+| catalog name |
++--------------+
+| catalog1 |
++--------------+
+1 row in set
+```
## SHOW CURRENT CATALOG
diff --git a/flink-table/flink-sql-client/src/test/resources/sql/catalog_database.q b/flink-table/flink-sql-client/src/test/resources/sql/catalog_database.q
index cde20202ee3..50f61a566de 100644
--- a/flink-table/flink-sql-client/src/test/resources/sql/catalog_database.q
+++ b/flink-table/flink-sql-client/src/test/resources/sql/catalog_database.q
@@ -62,6 +62,42 @@ show catalogs;
2 rows in set
!ok
+show catalogs like '%c1';
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs not like 'default%';
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs ilike '%c1';
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs not ilike 'default%';
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
show current catalog;
+----------------------+
| current catalog name |
diff --git a/flink-table/flink-sql-gateway/src/test/resources/sql/catalog_database.q b/flink-table/flink-sql-gateway/src/test/resources/sql/catalog_database.q
index 3053d4d6b05..5ea0ca9805c 100644
--- a/flink-table/flink-sql-gateway/src/test/resources/sql/catalog_database.q
+++ b/flink-table/flink-sql-gateway/src/test/resources/sql/catalog_database.q
@@ -69,6 +69,46 @@ show catalogs;
2 rows in set
!ok
+show catalogs like '%c1';
+!output
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs not like 'default%';
+!output
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs ilike '%c1';
+!output
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
+show catalogs not ilike 'default%';
+!output
++--------------+
+| catalog name |
++--------------+
+| c1 |
++--------------+
+1 row in set
+!ok
+
show current catalog;
!output
+----------------------+
diff --git a/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl b/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
index 9e7bad6206c..38c4c85227d 100644
--- a/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
+++ b/flink-table/flink-sql-parser/src/main/codegen/includes/parserImpls.ftl
@@ -50,11 +50,40 @@ boolean IfNotExistsOpt() :
*/
SqlShowCatalogs SqlShowCatalogs() :
{
+ SqlParserPos pos;
+ String likeType = null;
+ SqlCharStringLiteral likeLiteral = null;
+ boolean notLike = false;
}
{
<SHOW> <CATALOGS>
+ { pos = getPos(); }
+ [
+ [
+ <NOT>
+ {
+ notLike = true;
+ }
+ ]
+ (
+ <LIKE>
+ {
+ likeType = "LIKE";
+ }
+ |
+ <ILIKE>
+ {
+ likeType = "ILIKE";
+ }
+ )
+ <QUOTED_STRING>
+ {
+ String likeCondition = SqlParserUtil.parseString(token.image);
+ likeLiteral = SqlLiteral.createCharString(likeCondition, getPos());
+ }
+ ]
{
- return new SqlShowCatalogs(getPos());
+ return new SqlShowCatalogs(pos.plus(getPos()), likeType, likeLiteral, notLike);
}
}
diff --git a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlShowCatalogs.java b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlShowCatalogs.java
index a6a0e64e805..09270267658 100644
--- a/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlShowCatalogs.java
+++ b/flink-table/flink-sql-parser/src/main/java/org/apache/flink/sql/parser/dql/SqlShowCatalogs.java
@@ -18,25 +18,27 @@
package org.apache.flink.sql.parser.dql;
-import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlCharStringLiteral;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
-import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.parser.SqlParserPos;
-import java.util.Collections;
-import java.util.List;
-
-/** SHOW CATALOGS sql call. */
-public class SqlShowCatalogs extends SqlCall {
+/**
+ * SHOW Catalogs sql call. The full syntax for show catalogs is as followings:
+ *
+ * <pre>{@code
+ * SHOW CATALOGS [ [NOT] (LIKE | ILIKE) <sql_like_pattern> ]
+ * }</pre>
+ */
+public class SqlShowCatalogs extends SqlShowCall {
public static final SqlSpecialOperator OPERATOR =
new SqlSpecialOperator("SHOW CATALOGS", SqlKind.OTHER);
- public SqlShowCatalogs(SqlParserPos pos) {
- super(pos);
+ public SqlShowCatalogs(
+ SqlParserPos pos, String likeType, SqlCharStringLiteral likeLiteral, boolean notLike) {
+ super(pos, null, null, likeType, likeLiteral, notLike);
}
@Override
@@ -45,12 +47,7 @@ public class SqlShowCatalogs extends SqlCall {
}
@Override
- public List<SqlNode> getOperandList() {
- return Collections.emptyList();
- }
-
- @Override
- public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
- writer.keyword("SHOW CATALOGS");
+ String getOperationName() {
+ return "SHOW CATALOGS";
}
}
diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
index c732859e5a8..39c9a41c18c 100644
--- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
+++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlParserImplTest.java
@@ -52,6 +52,20 @@ class FlinkSqlParserImplTest extends SqlParserTest {
@Test
void testShowCatalogs() {
sql("show catalogs").ok("SHOW CATALOGS");
+
+ sql("show catalogs like '%'").ok("SHOW CATALOGS LIKE '%'");
+ sql("show catalogs not like '%'").ok("SHOW CATALOGS NOT LIKE '%'");
+
+ sql("show catalogs ilike '%'").ok("SHOW CATALOGS ILIKE '%'");
+ sql("show catalogs not ilike '%'").ok("SHOW CATALOGS NOT ILIKE '%'");
+
+ sql("show catalogs ^likes^").fails("(?s).*Encountered \"likes\" at line 1, column 15.\n.*");
+ sql("show catalogs not ^likes^")
+ .fails("(?s).*Encountered \"likes\" at line 1, column 19" + ".\n" + ".*");
+ sql("show catalogs ^ilikes^")
+ .fails("(?s).*Encountered \"ilikes\" at line 1, column 15.\n.*");
+ sql("show catalogs not ^ilikes^")
+ .fails("(?s).*Encountered \"ilikes\" at line 1, column 19" + ".\n" + ".*");
}
@Test
diff --git a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCatalogsOperation.java b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCatalogsOperation.java
index df5c0fc3b0f..c43f9b9ed64 100644
--- a/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCatalogsOperation.java
+++ b/flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/operations/ShowCatalogsOperation.java
@@ -19,23 +19,41 @@
package org.apache.flink.table.operations;
import org.apache.flink.annotation.Internal;
-import org.apache.flink.table.api.internal.TableResultInternal;
+import org.apache.flink.table.catalog.CatalogManager;
+import org.apache.flink.table.operations.utils.ShowLikeOperator;
-import static org.apache.flink.table.api.internal.TableResultUtils.buildStringArrayResult;
+import javax.annotation.Nullable;
-/** Operation to describe a SHOW CATALOGS statement. */
+import java.util.Collection;
+
+/**
+ * Operation to describe a SHOW CATALOGS statement. The full syntax for SHOW CATALOGS is as
+ * followings:
+ *
+ * <pre>{@code
+ * SHOW CATALOGS [ [NOT] (LIKE | ILIKE) <sql_like_pattern> ]
+ * }</pre>
+ */
@Internal
-public class ShowCatalogsOperation implements ShowOperation {
+public class ShowCatalogsOperation extends AbstractShowOperation {
+
+ public ShowCatalogsOperation(@Nullable ShowLikeOperator likeOp) {
+ super(null, null, likeOp);
+ }
@Override
- public String asSummaryString() {
+ protected String getOperationName() {
return "SHOW CATALOGS";
}
@Override
- public TableResultInternal execute(Context ctx) {
- String[] catalogs =
- ctx.getCatalogManager().listCatalogs().stream().sorted().toArray(String[]::new);
- return buildStringArrayResult("catalog name", catalogs);
+ protected String getColumnName() {
+ return "catalog name";
+ }
+
+ @Override
+ protected Collection<String> retrieveDataForTableResult(Context ctx) {
+ final CatalogManager catalogManager = ctx.getCatalogManager();
+ return catalogManager.listCatalogs();
}
}
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/SqlNodeToOperationConversion.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/SqlNodeToOperationConversion.java
index d63f17e4433..0248f118b8a 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/SqlNodeToOperationConversion.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/SqlNodeToOperationConversion.java
@@ -65,7 +65,6 @@ import org.apache.flink.sql.parser.dml.SqlStatementSet;
import org.apache.flink.sql.parser.dql.SqlLoadModule;
import org.apache.flink.sql.parser.dql.SqlRichDescribeTable;
import org.apache.flink.sql.parser.dql.SqlRichExplain;
-import org.apache.flink.sql.parser.dql.SqlShowCatalogs;
import org.apache.flink.sql.parser.dql.SqlShowColumns;
import org.apache.flink.sql.parser.dql.SqlShowCreateTable;
import org.apache.flink.sql.parser.dql.SqlShowCreateView;
@@ -125,7 +124,6 @@ import org.apache.flink.table.operations.ModifyType;
import org.apache.flink.table.operations.NopOperation;
import org.apache.flink.table.operations.Operation;
import org.apache.flink.table.operations.QueryOperation;
-import org.apache.flink.table.operations.ShowCatalogsOperation;
import org.apache.flink.table.operations.ShowColumnsOperation;
import org.apache.flink.table.operations.ShowCreateTableOperation;
import org.apache.flink.table.operations.ShowCreateViewOperation;
@@ -280,8 +278,6 @@ public class SqlNodeToOperationConversion {
return Optional.of(converter.convertDropCatalog((SqlDropCatalog) validated));
} else if (validated instanceof SqlLoadModule) {
return Optional.of(converter.convertLoadModule((SqlLoadModule) validated));
- } else if (validated instanceof SqlShowCatalogs) {
- return Optional.of(converter.convertShowCatalogs((SqlShowCatalogs) validated));
} else if (validated instanceof SqlShowCurrentCatalog) {
return Optional.of(
converter.convertShowCurrentCatalog((SqlShowCurrentCatalog) validated));
@@ -911,11 +907,6 @@ public class SqlNodeToOperationConversion {
return new AlterDatabaseOperation(catalogName, databaseName, catalogDatabase);
}
- /** Convert SHOW CATALOGS statement. */
- private Operation convertShowCatalogs(SqlShowCatalogs sqlShowCatalogs) {
- return new ShowCatalogsOperation();
- }
-
/** Convert SHOW CURRENT CATALOG statement. */
private Operation convertShowCurrentCatalog(SqlShowCurrentCatalog sqlShowCurrentCatalog) {
return new ShowCurrentCatalogOperation();
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
index 5950f2e2c5d..a2126791e4b 100644
--- a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlNodeConverters.java
@@ -66,6 +66,7 @@ public class SqlNodeConverters {
register(new SqlDropMaterializedTableConverter());
register(new SqlShowTablesConverter());
register(new SqlShowViewsConverter());
+ register(new SqlShowCatalogsConverter());
}
/**
diff --git a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCatalogsConverter.java b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCatalogsConverter.java
new file mode 100644
index 00000000000..2f3301cd212
--- /dev/null
+++ b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/operations/converters/SqlShowCatalogsConverter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.table.planner.operations.converters;
+
+import org.apache.flink.sql.parser.dql.SqlShowCatalogs;
+import org.apache.flink.table.operations.Operation;
+import org.apache.flink.table.operations.ShowCatalogsOperation;
+import org.apache.flink.table.operations.utils.LikeType;
+import org.apache.flink.table.operations.utils.ShowLikeOperator;
+
+/** A converter for {@link SqlShowCatalogs}. */
+public class SqlShowCatalogsConverter implements SqlNodeConverter<SqlShowCatalogs> {
+
+ @Override
+ public Operation convertSqlNode(SqlShowCatalogs sqlShowCatalogs, ConvertContext context) {
+ final ShowLikeOperator likeOp =
+ ShowLikeOperator.of(
+ LikeType.of(sqlShowCatalogs.getLikeType(), sqlShowCatalogs.isNotLike()),
+ sqlShowCatalogs.getLikeSqlPattern());
+ return new ShowCatalogsOperation(likeOp);
+ }
+}
diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
index 5cf2bcecff0..e4dabb3c4d2 100644
--- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
+++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/operations/SqlOtherOperationConverterTest.java
@@ -23,6 +23,7 @@ import org.apache.flink.table.api.ValidationException;
import org.apache.flink.table.operations.DescribeCatalogOperation;
import org.apache.flink.table.operations.LoadModuleOperation;
import org.apache.flink.table.operations.Operation;
+import org.apache.flink.table.operations.ShowCatalogsOperation;
import org.apache.flink.table.operations.ShowCreateCatalogOperation;
import org.apache.flink.table.operations.ShowDatabasesOperation;
import org.apache.flink.table.operations.ShowFunctionsOperation;
@@ -183,6 +184,35 @@ public class SqlOtherOperationConverterTest extends SqlNodeToOperationConversion
assertThat(useModulesOperation.asSummaryString()).isEqualTo("USE MODULES: [x, y, z]");
}
+ @ParameterizedTest(name = "{index}: {0}")
+ @MethodSource("inputForShowCatalogsTest")
+ void testShowCatalogs(String sql, ShowCatalogsOperation expected, String expectedSummary) {
+ Operation operation = parse(sql);
+ assertThat(operation).isInstanceOf(ShowCatalogsOperation.class).isEqualTo(expected);
+ assertThat(operation.asSummaryString()).isEqualTo(expectedSummary);
+ }
+
+ private static Stream<Arguments> inputForShowCatalogsTest() {
+ return Stream.of(
+ Arguments.of("show catalogs", new ShowCatalogsOperation(null), "SHOW CATALOGS"),
+ Arguments.of(
+ "show catalogs like 'c%'",
+ new ShowCatalogsOperation(ShowLikeOperator.of(LikeType.LIKE, "c%")),
+ "SHOW CATALOGS LIKE 'c%'"),
+ Arguments.of(
+ "show catalogs not like 'c%'",
+ new ShowCatalogsOperation(ShowLikeOperator.of(LikeType.NOT_LIKE, "c%")),
+ "SHOW CATALOGS NOT LIKE 'c%'"),
+ Arguments.of(
+ "show catalogs ilike 'c%'",
+ new ShowCatalogsOperation(ShowLikeOperator.of(LikeType.ILIKE, "c%")),
+ "SHOW CATALOGS ILIKE 'c%'"),
+ Arguments.of(
+ "show catalogs not ilike 'c%'",
+ new ShowCatalogsOperation(ShowLikeOperator.of(LikeType.NOT_ILIKE, "c%")),
+ "SHOW CATALOGS NOT ILIKE 'c%'"));
+ }
+
@Test
void testShowModules() {
final String sql = "SHOW MODULES";