This is an automated email from the ASF dual-hosted git repository. duanzhengqiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push: new 26fdf020748 Optimize database admin executor creator (#34608) 26fdf020748 is described below commit 26fdf0207481d21bcecd43a74b85a46ff4619834 Author: jiangML <1060319...@qq.com> AuthorDate: Sun Feb 9 11:44:16 2025 +0800 Optimize database admin executor creator (#34608) * Optimize database admin executor creator * Fix test error * Optimize OpenGaussAdminExecutorCreator * Fix e2e test error --- .../opengauss/OpenGaussStatisticsCollector.java | 51 ++++++++++++ ...cs.collector.DialectDatabaseStatisticsCollector | 1 + .../MySQLInformationSchemaExecutorFactory.java | 16 +++- .../admin/OpenGaussAdminExecutorCreator.java | 94 ++++++++++++++++++---- .../admin/OpenGaussAdminExecutorCreatorTest.java | 41 ++++++++-- .../admin/OpenGaussAdminExecutorFactoryTest.java | 49 +++++++---- .../admin/PostgreSQLAdminExecutorCreator.java | 84 ++++++++++++++++--- 7 files changed, 287 insertions(+), 49 deletions(-) diff --git a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/statistics/collector/opengauss/OpenGaussStatisticsCollector.java b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/statistics/collector/opengauss/OpenGaussStatisticsCollector.java new file mode 100644 index 00000000000..461c6bdebc7 --- /dev/null +++ b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/statistics/collector/opengauss/OpenGaussStatisticsCollector.java @@ -0,0 +1,51 @@ +/* + * 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.shardingsphere.infra.metadata.statistics.collector.opengauss; + +import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData; +import org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector; +import org.apache.shardingsphere.infra.metadata.statistics.collector.postgresql.PostgreSQLStatisticsCollector; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * Statistics collector for openGauss. + */ +public final class OpenGaussStatisticsCollector implements DialectDatabaseStatisticsCollector { + + private final PostgreSQLStatisticsCollector delegated = new PostgreSQLStatisticsCollector(); + + @Override + public Map<String, Collection<String>> getStatisticsSchemaTables() { + return delegated.getStatisticsSchemaTables(); + } + + @Override + public Optional<Collection<Map<String, Object>>> collectRowColumnValues(final String databaseName, final String schemaName, final String tableName, + final ShardingSphereMetaData metaData) throws SQLException { + return delegated.collectRowColumnValues(databaseName, schemaName, tableName, metaData); + } + + @Override + public String getDatabaseType() { + return "openGauss"; + } +} diff --git a/infra/common/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector b/infra/common/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector index c1ee220d54d..41ca193f35a 100644 --- a/infra/common/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector +++ b/infra/common/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector @@ -17,3 +17,4 @@ org.apache.shardingsphere.infra.metadata.statistics.collector.shardingsphere.ShardingSphereStatisticsCollector org.apache.shardingsphere.infra.metadata.statistics.collector.postgresql.PostgreSQLStatisticsCollector +org.apache.shardingsphere.infra.metadata.statistics.collector.opengauss.OpenGaussStatisticsCollector diff --git a/proxy/backend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/MySQLInformationSchemaExecutorFactory.java b/proxy/backend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/MySQLInformationSchemaExecutorFactory.java index c13ddef2562..0157d62bf18 100644 --- a/proxy/backend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/MySQLInformationSchemaExecutorFactory.java +++ b/proxy/backend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/MySQLInformationSchemaExecutorFactory.java @@ -26,7 +26,11 @@ import org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.info import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment; import org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; /** @@ -50,12 +54,22 @@ public final class MySQLInformationSchemaExecutorFactory { return Optional.empty(); } String tableName = ((SimpleTableSegment) sqlStatement.getFrom().get()).getTableName().getIdentifier().getValue(); + Map<String, Collection<String>> selectedSchemaTables = Collections.singletonMap("information_schema", Collections.singletonList(tableName)); if (SCHEMATA_TABLE.equalsIgnoreCase(tableName)) { return Optional.of(new SelectInformationSchemataExecutor(sqlStatement, sql, parameters)); } - if (SystemSchemaManager.isSystemTable("mysql", "information_schema", tableName)) { + if (isSelectSystemTable(selectedSchemaTables)) { return Optional.of(new DefaultDatabaseMetaDataExecutor(sql, parameters)); } return Optional.empty(); } + + private static boolean isSelectSystemTable(final Map<String, Collection<String>> selectedSchemaTableNames) { + for (Entry<String, Collection<String>> each : selectedSchemaTableNames.entrySet()) { + if (!SystemSchemaManager.isSystemTable("mysql", each.getKey(), each.getValue())) { + return false; + } + } + return true; + } } diff --git a/proxy/backend/type/opengauss/src/main/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreator.java b/proxy/backend/type/opengauss/src/main/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreator.java index 6e91bdac1e4..45db5398953 100644 --- a/proxy/backend/type/opengauss/src/main/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreator.java +++ b/proxy/backend/type/opengauss/src/main/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreator.java @@ -17,23 +17,34 @@ package org.apache.shardingsphere.proxy.backend.opengauss.handler.admin; +import com.cedarsoftware.util.CaseInsensitiveMap; import com.cedarsoftware.util.CaseInsensitiveSet; +import com.google.common.base.Strings; import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext; import org.apache.shardingsphere.infra.binder.context.type.TableAvailable; +import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader; +import org.apache.shardingsphere.infra.database.core.type.DatabaseType; import org.apache.shardingsphere.infra.metadata.database.schema.manager.SystemSchemaManager; +import org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.AbstractDatabaseMetaDataExecutor.DefaultDatabaseMetaDataExecutor; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutorCreator; import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.PostgreSQLAdminExecutorCreator; import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment; import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableNameSegment; import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement; import org.apache.shardingsphere.sql.parser.statement.core.statement.dal.ShowStatement; import org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; /** @@ -43,7 +54,7 @@ public final class OpenGaussAdminExecutorCreator implements DatabaseAdminExecuto private static final Collection<String> SYSTEM_CATALOG_QUERY_EXPRESSIONS = new CaseInsensitiveSet<>(); - private static final Collection<String> SYSTEM_CATALOG_TABLES = new CaseInsensitiveSet<>(); + private static final Map<String, Collection<String>> SCHEMA_TABLES = new CaseInsensitiveMap<>(); static { SYSTEM_CATALOG_QUERY_EXPRESSIONS.add("VERSION()"); @@ -56,11 +67,8 @@ public final class OpenGaussAdminExecutorCreator implements DatabaseAdminExecuto SYSTEM_CATALOG_QUERY_EXPRESSIONS.add("pg_catalog.intervaltonum()"); SYSTEM_CATALOG_QUERY_EXPRESSIONS.add("pg_catalog.intervaltonum(pg_catalog.gs_password_deadline())"); SYSTEM_CATALOG_QUERY_EXPRESSIONS.add("pg_catalog.gs_password_notifytime()"); - SYSTEM_CATALOG_TABLES.add("pg_class"); - SYSTEM_CATALOG_TABLES.add("pg_namespace"); - SYSTEM_CATALOG_TABLES.add("pg_database"); - SYSTEM_CATALOG_TABLES.add("pg_tables"); - SYSTEM_CATALOG_TABLES.add("pg_roles"); + SCHEMA_TABLES.put("pg_catalog", new CaseInsensitiveSet<>(Arrays.asList("pg_class", "pg_namespace", "pg_database", "pg_tables", "pg_roles"))); + SCHEMA_TABLES.put("shardingsphere", new CaseInsensitiveSet<>(Arrays.asList("cluster_information", "sharding_table_statistics"))); } private final PostgreSQLAdminExecutorCreator delegated = new PostgreSQLAdminExecutorCreator(); @@ -76,18 +84,68 @@ public final class OpenGaussAdminExecutorCreator implements DatabaseAdminExecuto @Override public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext, final String sql, final String databaseName, final List<Object> parameters) { - if (isSQLFederationSystemCatalogQuery(sqlStatementContext) || isSQLFederationSystemCatalogQueryExpressions(sqlStatementContext)) { + Map<String, Collection<String>> selectedSchemaTables = sqlStatementContext instanceof TableAvailable ? getSelectedSchemaTables(sqlStatementContext) : Collections.emptyMap(); + if (isSQLFederationSystemCatalogQuery(selectedSchemaTables) || isSQLFederationSystemCatalogQueryExpressions(sqlStatementContext)) { return Optional.of(new OpenGaussSystemCatalogAdminQueryExecutor(sqlStatementContext, sql, databaseName, parameters)); } - if (isPassThroughSystemCatalogQuery(sqlStatementContext)) { + if (isPassThroughSystemCatalogQuery(selectedSchemaTables)) { return Optional.of(new DefaultDatabaseMetaDataExecutor(sql, parameters)); } return delegated.create(sqlStatementContext, sql, databaseName, parameters); } - private boolean isSQLFederationSystemCatalogQuery(final SQLStatementContext sqlStatementContext) { - Collection<String> tableNames = sqlStatementContext instanceof TableAvailable ? ((TableAvailable) sqlStatementContext).getTablesContext().getTableNames() : Collections.emptyList(); - return !tableNames.isEmpty() && SYSTEM_CATALOG_TABLES.containsAll(tableNames); + private Map<String, Collection<String>> getSelectedSchemaTables(final SQLStatementContext sqlStatementContext) { + Map<String, Collection<String>> result = new CaseInsensitiveMap<>(); + for (SimpleTableSegment each : ((TableAvailable) sqlStatementContext).getTablesContext().getSimpleTables()) { + TableNameSegment tableNameSegment = each.getTableName(); + String schemaName = tableNameSegment.getTableBoundInfo().map(optional -> optional.getOriginalSchema().getValue()).orElse(null); + schemaName = Strings.isNullOrEmpty(schemaName) ? each.getOwner().map(optional -> optional.getIdentifier().getValue()).orElse(null) : schemaName; + if (!Strings.isNullOrEmpty(schemaName)) { + Collection<String> tables = result.getOrDefault(schemaName, new CaseInsensitiveSet<>()); + tables.add(tableNameSegment.getIdentifier().getValue()); + result.put(schemaName, tables); + } + } + return result; + } + + private boolean isSQLFederationSystemCatalogQuery(final Map<String, Collection<String>> selectedSchemaTables) { + if (isSelectedStatisticsSystemTable(selectedSchemaTables)) { + return true; + } + if (selectedSchemaTables.isEmpty()) { + return false; + } + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!SCHEMA_TABLES.containsKey(each.getKey())) { + return false; + } + if (!SCHEMA_TABLES.get(each.getKey()).containsAll(each.getValue())) { + return false; + } + } + return true; + } + + private boolean isSelectedStatisticsSystemTable(final Map<String, Collection<String>> selectedSchemaTables) { + if (selectedSchemaTables.isEmpty()) { + return false; + } + DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "openGauss"); + Optional<DialectDatabaseStatisticsCollector> dialectStatisticsCollector = DatabaseTypedSPILoader.findService(DialectDatabaseStatisticsCollector.class, databaseType); + if (!dialectStatisticsCollector.isPresent()) { + return false; + } + Map<String, Collection<String>> statisticalSchemaTables = dialectStatisticsCollector.get().getStatisticsSchemaTables(); + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!statisticalSchemaTables.containsKey(each.getKey())) { + return false; + } + if (!statisticalSchemaTables.get(each.getKey()).containsAll(each.getValue())) { + return false; + } + } + return true; } private boolean isSQLFederationSystemCatalogQueryExpressions(final SQLStatementContext sqlStatementContext) { @@ -100,10 +158,16 @@ public final class OpenGaussAdminExecutorCreator implements DatabaseAdminExecuto && SYSTEM_CATALOG_QUERY_EXPRESSIONS.contains(((ExpressionProjectionSegment) projections.iterator().next()).getText()); } - private boolean isPassThroughSystemCatalogQuery(final SQLStatementContext sqlStatementContext) { - Collection<String> tableNames = sqlStatementContext instanceof TableAvailable ? ((TableAvailable) sqlStatementContext).getTablesContext().getTableNames() : Collections.emptyList(); - return !tableNames.isEmpty() && (SystemSchemaManager.isSystemTable("opengauss", "information_schema", tableNames) - || SystemSchemaManager.isSystemTable("opengauss", "pg_catalog", tableNames)); + private boolean isPassThroughSystemCatalogQuery(final Map<String, Collection<String>> selectedSchemaTables) { + if (selectedSchemaTables.isEmpty()) { + return false; + } + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!SystemSchemaManager.isSystemTable("opengauss", each.getKey(), each.getValue())) { + return false; + } + } + return true; } @Override diff --git a/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreatorTest.java b/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreatorTest.java index a2989580f54..2b67d868c02 100644 --- a/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreatorTest.java +++ b/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorCreatorTest.java @@ -20,9 +20,18 @@ package org.apache.shardingsphere.proxy.backend.opengauss.handler.admin; import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext; import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext; import org.apache.shardingsphere.infra.binder.context.type.TableAvailable; +import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader; +import org.apache.shardingsphere.infra.database.core.type.DatabaseType; +import org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor; import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.TableSegmentBoundInfo; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment; +import org.apache.shardingsphere.test.mock.AutoMockExtension; +import org.apache.shardingsphere.test.mock.StaticMockSettings; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.Collections; import java.util.Optional; @@ -36,12 +45,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; +@ExtendWith(AutoMockExtension.class) +@StaticMockSettings(DatabaseTypedSPILoader.class) class OpenGaussAdminExecutorCreatorTest { @Test void assertCreateExecutorForSelectDatabase() { - SelectStatementContext selectStatementContext = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); - when(selectStatementContext.getTablesContext().getTableNames()).thenReturn(Collections.singletonList("pg_database")); + setUp("pg_database"); + SelectStatementContext selectStatementContext = mockSelectStatementContext("pg_database"); Optional<DatabaseAdminExecutor> actual = new OpenGaussAdminExecutorCreator() .create(selectStatementContext, "select datname, datcompatibility from pg_database where datname = 'sharding_db'", "postgres", Collections.emptyList()); assertTrue(actual.isPresent()); @@ -50,8 +61,8 @@ class OpenGaussAdminExecutorCreatorTest { @Test void assertCreateExecutorForSelectTables() { - SelectStatementContext selectStatementContext = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); - when(selectStatementContext.getTablesContext().getTableNames()).thenReturn(Collections.singletonList("pg_tables")); + setUp("pg_tables"); + SelectStatementContext selectStatementContext = mockSelectStatementContext("pg_tables"); Optional<DatabaseAdminExecutor> actual = new OpenGaussAdminExecutorCreator() .create(selectStatementContext, "select schemaname, tablename from pg_tables where schemaname = 'sharding_db'", "postgres", Collections.emptyList()); assertTrue(actual.isPresent()); @@ -60,8 +71,8 @@ class OpenGaussAdminExecutorCreatorTest { @Test void assertCreateExecutorForSelectRoles() { - SelectStatementContext selectStatementContext = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); - when(selectStatementContext.getTablesContext().getTableNames()).thenReturn(Collections.singletonList("pg_roles")); + setUp("pg_roles"); + SelectStatementContext selectStatementContext = mockSelectStatementContext("pg_roles"); Optional<DatabaseAdminExecutor> actual = new OpenGaussAdminExecutorCreator() .create(selectStatementContext, "select rolname from pg_roles", "postgres", Collections.emptyList()); assertTrue(actual.isPresent()); @@ -85,4 +96,22 @@ class OpenGaussAdminExecutorCreatorTest { assertThat(creator.create(sqlStatementContext), is(Optional.empty())); assertThat(creator.create(sqlStatementContext, "", "", Collections.emptyList()), is(Optional.empty())); } + + private void setUp(final String tableName) { + DialectDatabaseStatisticsCollector statisticsCollector = mock(DialectDatabaseStatisticsCollector.class); + when(statisticsCollector.getStatisticsSchemaTables()).thenReturn(Collections.singletonMap("pg_catalog", Collections.singletonList(tableName))); + when(DatabaseTypedSPILoader.findService(DialectDatabaseStatisticsCollector.class, TypedSPILoader.getService(DatabaseType.class, "openGauss"))).thenReturn(Optional.of(statisticsCollector)); + } + + private SelectStatementContext mockSelectStatementContext(final String tableName) { + SimpleTableSegment simpleTableSegment = mock(SimpleTableSegment.class, RETURNS_DEEP_STUBS); + when(simpleTableSegment.getTableName().getIdentifier().getValue()).thenReturn(tableName); + TableSegmentBoundInfo tableSegmentBoundInfo = mock(TableSegmentBoundInfo.class, RETURNS_DEEP_STUBS); + when(tableSegmentBoundInfo.getOriginalSchema().getValue()).thenReturn("pg_catalog"); + when(simpleTableSegment.getTableName().getTableBoundInfo()).thenReturn(Optional.of(tableSegmentBoundInfo)); + SelectStatementContext result = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); + when(result.getTablesContext().getSimpleTables()).thenReturn(Collections.singletonList(simpleTableSegment)); + when(result.getTablesContext().getTableNames()).thenReturn(Collections.singletonList(tableName)); + return result; + } } diff --git a/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorFactoryTest.java b/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorFactoryTest.java index 2d7f9536c04..da250b123aa 100644 --- a/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorFactoryTest.java +++ b/proxy/backend/type/opengauss/src/test/java/org/apache/shardingsphere/proxy/backend/opengauss/handler/admin/OpenGaussAdminExecutorFactoryTest.java @@ -19,16 +19,22 @@ package org.apache.shardingsphere.proxy.backend.opengauss.handler.admin; import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext; import org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext; +import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader; +import org.apache.shardingsphere.infra.database.core.type.DatabaseType; +import org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor; import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.PostgreSQLAdminExecutorCreator; -import org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.TableSegmentBoundInfo; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment; import org.apache.shardingsphere.sql.parser.statement.opengauss.dal.OpenGaussShowStatement; +import org.apache.shardingsphere.test.mock.AutoMockExtension; +import org.apache.shardingsphere.test.mock.StaticMockSettings; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.internal.configuration.plugins.Plugins; -import org.mockito.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.Optional; @@ -41,7 +47,8 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@ExtendWith(MockitoExtension.class) +@ExtendWith(AutoMockExtension.class) +@StaticMockSettings(DatabaseTypedSPILoader.class) class OpenGaussAdminExecutorFactoryTest { @Mock @@ -63,18 +70,6 @@ class OpenGaussAdminExecutorFactoryTest { assertTrue(actual.isPresent()); } - @Test - void assertNewInstanceWithSelectDatabase() { - SelectStatementContext sqlStatementContext = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); - SelectStatement statement = mock(SelectStatement.class, RETURNS_DEEP_STUBS); - when(sqlStatementContext.getSqlStatement()).thenReturn(statement); - when(sqlStatementContext.getTablesContext().getTableNames()).thenReturn(Collections.singletonList("pg_database")); - String sql = "select datcompatibility from pg_database where datname = 'sharding_db'"; - Optional<DatabaseAdminExecutor> actual = openGaussAdminExecutorFactory.create(sqlStatementContext, sql, "", Collections.emptyList()); - assertTrue(actual.isPresent()); - assertThat(actual.get(), instanceOf(OpenGaussSystemCatalogAdminQueryExecutor.class)); - } - @Test void assertNewInstanceWithOtherSQL() { SelectStatementContext sqlStatementContext = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); @@ -85,4 +80,28 @@ class OpenGaussAdminExecutorFactoryTest { assertTrue(actual.isPresent()); assertThat(actual.get(), is(expected)); } + + @Test + void assertNewInstanceWithSelectDatabase() { + DialectDatabaseStatisticsCollector statisticsCollector = mock(DialectDatabaseStatisticsCollector.class); + when(statisticsCollector.getStatisticsSchemaTables()).thenReturn(Collections.singletonMap("pg_catalog", Collections.singletonList("pg_database"))); + when(DatabaseTypedSPILoader.findService(DialectDatabaseStatisticsCollector.class, TypedSPILoader.getService(DatabaseType.class, "openGauss"))).thenReturn(Optional.of(statisticsCollector)); + SelectStatementContext sqlStatementContext = mockSelectStatementContext(); + String sql = "select datcompatibility from pg_database where datname = 'sharding_db'"; + Optional<DatabaseAdminExecutor> actual = openGaussAdminExecutorFactory.create(sqlStatementContext, sql, "", Collections.emptyList()); + assertTrue(actual.isPresent()); + assertThat(actual.get(), instanceOf(OpenGaussSystemCatalogAdminQueryExecutor.class)); + } + + private SelectStatementContext mockSelectStatementContext() { + SimpleTableSegment simpleTableSegment = mock(SimpleTableSegment.class, RETURNS_DEEP_STUBS); + when(simpleTableSegment.getTableName().getIdentifier().getValue()).thenReturn("pg_database"); + TableSegmentBoundInfo tableSegmentBoundInfo = mock(TableSegmentBoundInfo.class, RETURNS_DEEP_STUBS); + when(tableSegmentBoundInfo.getOriginalSchema().getValue()).thenReturn("pg_catalog"); + when(simpleTableSegment.getTableName().getTableBoundInfo()).thenReturn(Optional.of(tableSegmentBoundInfo)); + SelectStatementContext result = mock(SelectStatementContext.class, RETURNS_DEEP_STUBS); + when(result.getTablesContext().getSimpleTables()).thenReturn(Collections.singletonList(simpleTableSegment)); + when(result.getTablesContext().getTableNames()).thenReturn(Collections.singletonList("pg_database")); + return result; + } } diff --git a/proxy/backend/type/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/PostgreSQLAdminExecutorCreator.java b/proxy/backend/type/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/PostgreSQLAdminExecutorCreator.java index 866fd0a8182..124fe3d4728 100644 --- a/proxy/backend/type/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/PostgreSQLAdminExecutorCreator.java +++ b/proxy/backend/type/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/PostgreSQLAdminExecutorCreator.java @@ -17,8 +17,14 @@ package org.apache.shardingsphere.proxy.backend.postgresql.handler.admin; +import com.cedarsoftware.util.CaseInsensitiveMap; +import com.cedarsoftware.util.CaseInsensitiveSet; import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext; +import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader; +import org.apache.shardingsphere.infra.database.core.type.DatabaseType; import org.apache.shardingsphere.infra.metadata.database.schema.manager.SystemSchemaManager; +import org.apache.shardingsphere.infra.metadata.statistics.collector.DialectDatabaseStatisticsCollector; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.AbstractDatabaseMetaDataExecutor.DefaultDatabaseMetaDataExecutor; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor; import org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutorCreator; @@ -26,6 +32,7 @@ import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.PostgreSQLSetVariableAdminExecutor; import org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.PostgreSQLShowVariableExecutor; import org.apache.shardingsphere.sql.parser.statement.core.extractor.TableExtractor; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerSegment; import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment; import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment; import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment; @@ -35,11 +42,12 @@ import org.apache.shardingsphere.sql.parser.statement.core.statement.dal.SetStat import org.apache.shardingsphere.sql.parser.statement.core.statement.dal.ShowStatement; import org.apache.shardingsphere.sql.parser.statement.core.statement.dml.SelectStatement; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; /** @@ -47,11 +55,11 @@ import java.util.Optional; */ public final class PostgreSQLAdminExecutorCreator implements DatabaseAdminExecutorCreator { - private static final String PG_CLASS = "pg_class"; + private static final Map<String, Collection<String>> SCHEMA_TABLES = new CaseInsensitiveMap<>(); - private static final String PG_NAMESPACE = "pg_namespace"; - - private static final Collection<String> KERNEL_SUPPORTED_TABLES = Arrays.asList(PG_NAMESPACE, PG_CLASS); + static { + SCHEMA_TABLES.put("shardingsphere", new CaseInsensitiveSet<>(Arrays.asList("cluster_information", "sharding_table_statistics"))); + } @Override public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext) { @@ -66,12 +74,11 @@ public final class PostgreSQLAdminExecutorCreator implements DatabaseAdminExecut public Optional<DatabaseAdminExecutor> create(final SQLStatementContext sqlStatementContext, final String sql, final String databaseName, final List<Object> parameters) { SQLStatement sqlStatement = sqlStatementContext.getSqlStatement(); if (sqlStatement instanceof SelectStatement) { - Collection<String> selectedTableNames = getSelectedTableNames((SelectStatement) sqlStatement); - if (!selectedTableNames.isEmpty() && KERNEL_SUPPORTED_TABLES.containsAll(selectedTableNames)) { + Map<String, Collection<String>> selectedSchemaTables = getSelectedSchemaTables((SelectStatement) sqlStatement); + if (isSelectedStatisticsSystemTable(selectedSchemaTables) || isSelectedShardingSphereSystemTable(selectedSchemaTables)) { return Optional.empty(); } - if (!selectedTableNames.isEmpty() && (SystemSchemaManager.isSystemTable("postgresql", "information_schema", selectedTableNames) - || SystemSchemaManager.isSystemTable("postgresql", "pg_catalog", selectedTableNames))) { + if (isSelectSystemTable(selectedSchemaTables)) { return Optional.of(new DefaultDatabaseMetaDataExecutor(sql, parameters)); } } @@ -84,7 +91,7 @@ public final class PostgreSQLAdminExecutorCreator implements DatabaseAdminExecut return Optional.empty(); } - private Collection<String> getSelectedTableNames(final SelectStatement sqlStatement) { + private Map<String, Collection<String>> getSelectedSchemaTables(final SelectStatement sqlStatement) { TableExtractor extractor = new TableExtractor(); extractor.extractTablesFromSelect(sqlStatement); List<TableSegment> extracted = new LinkedList<>(extractor.getTableContext()); @@ -95,15 +102,68 @@ public final class PostgreSQLAdminExecutorCreator implements DatabaseAdminExecut extracted.addAll(subExtractor.getTableContext()); } } - List<String> result = new ArrayList<>(extracted.size()); + Map<String, Collection<String>> result = new CaseInsensitiveMap<>(); for (TableSegment each : extracted) { if (each instanceof SimpleTableSegment) { - result.add(((SimpleTableSegment) each).getTableName().getIdentifier().getValue()); + Optional<OwnerSegment> ownerSegment = ((SimpleTableSegment) each).getOwner(); + if (ownerSegment.isPresent()) { + Collection<String> tables = result.getOrDefault(ownerSegment.get().getIdentifier().getValue(), new CaseInsensitiveSet<>()); + tables.add(((SimpleTableSegment) each).getTableName().getIdentifier().getValue()); + result.put(ownerSegment.get().getIdentifier().getValue(), tables); + } } } return result; } + private boolean isSelectedStatisticsSystemTable(final Map<String, Collection<String>> selectedSchemaTables) { + if (selectedSchemaTables.isEmpty()) { + return false; + } + DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "PostgreSQL"); + Optional<DialectDatabaseStatisticsCollector> dialectStatisticsCollector = DatabaseTypedSPILoader.findService(DialectDatabaseStatisticsCollector.class, databaseType); + if (!dialectStatisticsCollector.isPresent()) { + return false; + } + Map<String, Collection<String>> statisticalSchemaTables = dialectStatisticsCollector.get().getStatisticsSchemaTables(); + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!statisticalSchemaTables.containsKey(each.getKey())) { + return false; + } + if (!statisticalSchemaTables.get(each.getKey()).containsAll(each.getValue())) { + return false; + } + } + return true; + } + + private boolean isSelectedShardingSphereSystemTable(final Map<String, Collection<String>> selectedSchemaTables) { + if (selectedSchemaTables.isEmpty()) { + return false; + } + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!SCHEMA_TABLES.containsKey(each.getKey())) { + return false; + } + if (!SCHEMA_TABLES.get(each.getKey()).containsAll(each.getValue())) { + return false; + } + } + return true; + } + + private boolean isSelectSystemTable(final Map<String, Collection<String>> selectedSchemaTables) { + if (selectedSchemaTables.isEmpty()) { + return false; + } + for (Entry<String, Collection<String>> each : selectedSchemaTables.entrySet()) { + if (!SystemSchemaManager.isSystemTable("postgresql", each.getKey(), each.getValue())) { + return false; + } + } + return true; + } + @Override public String getDatabaseType() { return "PostgreSQL";