diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 43449069cae9f..6da73ce3d9965 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -9,6 +9,7 @@ 1. Metadata: Add support for partition tables in PostgreSQL [#34346](https://github.com/apache/shardingsphere/pull/34346) 1. SQL Binder: Support select aggregation function sql bind in projection and having - [#34379](https://github.com/apache/shardingsphere/pull/34379) 1. Proxy Native: Add GraalVM Reachability Metadata and corresponding nativeTest for Firebird - [#34307](https://github.com/apache/shardingsphere/pull/34307) +1. Infra: Support for connecting to Presto's Memory Connector in ShardingSphere config - [#34432](https://github.com/apache/shardingsphere/pull/34432) ### Bug Fixes diff --git a/infra/database/type/presto/src/main/java/org/apache/shardingsphere/infra/database/presto/metadata/data/loader/PrestoMetaDataLoader.java b/infra/database/type/presto/src/main/java/org/apache/shardingsphere/infra/database/presto/metadata/data/loader/PrestoMetaDataLoader.java new file mode 100644 index 0000000000000..32eac9018b915 --- /dev/null +++ b/infra/database/type/presto/src/main/java/org/apache/shardingsphere/infra/database/presto/metadata/data/loader/PrestoMetaDataLoader.java @@ -0,0 +1,114 @@ +/* + * 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.database.presto.metadata.data.loader; + +import org.apache.shardingsphere.infra.database.core.metadata.data.loader.DialectMetaDataLoader; +import org.apache.shardingsphere.infra.database.core.metadata.data.loader.MetaDataLoaderMaterial; +import org.apache.shardingsphere.infra.database.core.metadata.data.model.ColumnMetaData; +import org.apache.shardingsphere.infra.database.core.metadata.data.model.SchemaMetaData; +import org.apache.shardingsphere.infra.database.core.metadata.data.model.TableMetaData; +import org.apache.shardingsphere.infra.database.core.metadata.database.datatype.DataTypeRegistry; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Meta data loader for Presto. + * As of the Memory Connector of prestodb/presto 0.290, the table `INFORMATION_SCHEMA.INDEXES` does not exist, + * and `INFORMATION_SCHEMA.COLUMNS` does not have a column `IS_VISIBLE`. + * The current implementation does not record the table's index, primary keys, generated info, or column visibility. + */ +public final class PrestoMetaDataLoader implements DialectMetaDataLoader { + + @Override + public Collection load(final MetaDataLoaderMaterial material) throws SQLException { + Collection tableMetaDataList = new LinkedList<>(); + try (Connection connection = material.getDataSource().getConnection()) { + Map> columnMetaDataMap = loadColumnMetaDataMap(connection, material.getActualTableNames()); + for (Map.Entry> entry : columnMetaDataMap.entrySet()) { + tableMetaDataList.add(new TableMetaData(entry.getKey(), entry.getValue(), Collections.emptyList(), Collections.emptyList())); + } + } + return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaDataList)); + } + + @SuppressWarnings("SqlSourceToSinkFlow") + private Map> loadColumnMetaDataMap(final Connection connection, final Collection tables) throws SQLException { + Map> result = new HashMap<>(); + try (PreparedStatement preparedStatement = connection.prepareStatement(getTableMetaDataSQL(tables))) { + preparedStatement.setString(1, connection.getCatalog()); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + ColumnMetaData columnMetaData = loadColumnMetaData(resultSet); + if (!result.containsKey(tableName)) { + result.put(tableName, new LinkedList<>()); + } + result.get(tableName).add(columnMetaData); + } + } + } + return result; + } + + private String getTableMetaDataSQL(final Collection tables) { + if (tables.isEmpty()) { + return "SELECT TABLE_CATALOG,\n" + + " TABLE_NAME,\n" + + " COLUMN_NAME,\n" + + " DATA_TYPE,\n" + + " ORDINAL_POSITION,\n" + + " IS_NULLABLE\n" + + "FROM INFORMATION_SCHEMA.COLUMNS\n" + + "WHERE TABLE_CATALOG = ?\n" + + "ORDER BY ORDINAL_POSITION"; + } + String collect = tables.stream().map(each -> String.format("'%s'", each).toUpperCase()).collect(Collectors.joining(",")); + return String.format("SELECT TABLE_CATALOG,\n" + + " TABLE_NAME,\n" + + " COLUMN_NAME,\n" + + " DATA_TYPE,\n" + + " ORDINAL_POSITION,\n" + + " IS_NULLABLE\n" + + "FROM INFORMATION_SCHEMA.COLUMNS\n" + + "WHERE TABLE_CATALOG = ?\n" + + " AND UPPER(TABLE_NAME) IN (%s)\n" + + "ORDER BY ORDINAL_POSITION", collect); + } + + private ColumnMetaData loadColumnMetaData(final ResultSet resultSet) throws SQLException { + String columnName = resultSet.getString("COLUMN_NAME"); + String dataType = resultSet.getString("DATA_TYPE"); + boolean isNullable = "YES".equals(resultSet.getString("IS_NULLABLE")); + return new ColumnMetaData(columnName, DataTypeRegistry.getDataType(getDatabaseType(), dataType).orElse(Types.OTHER), Boolean.FALSE, Boolean.FALSE, false, Boolean.TRUE, false, isNullable); + } + + @Override + public String getDatabaseType() { + return "Presto"; + } +} diff --git a/infra/database/type/presto/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.database.core.metadata.data.loader.DialectMetaDataLoader b/infra/database/type/presto/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.database.core.metadata.data.loader.DialectMetaDataLoader new file mode 100644 index 0000000000000..ce79600150194 --- /dev/null +++ b/infra/database/type/presto/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.database.core.metadata.data.loader.DialectMetaDataLoader @@ -0,0 +1,18 @@ +# +# 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. +# + +org.apache.shardingsphere.infra.database.presto.metadata.data.loader.PrestoMetaDataLoader diff --git a/infra/database/type/presto/src/test/java/org/apache/shardingsphere/infra/database/presto/database/PrestoDatabaseMetaDataTest.java b/infra/database/type/presto/src/test/java/org/apache/shardingsphere/infra/database/presto/database/PrestoDatabaseMetaDataTest.java new file mode 100644 index 0000000000000..ff4e4d34b9158 --- /dev/null +++ b/infra/database/type/presto/src/test/java/org/apache/shardingsphere/infra/database/presto/database/PrestoDatabaseMetaDataTest.java @@ -0,0 +1,44 @@ +/* + * 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.database.presto.database; + +import org.apache.shardingsphere.infra.database.core.metadata.database.DialectDatabaseMetaData; +import org.apache.shardingsphere.infra.database.core.metadata.database.enums.NullsOrderType; +import org.apache.shardingsphere.infra.database.core.metadata.database.enums.QuoteCharacter; +import org.apache.shardingsphere.infra.database.core.spi.DatabaseTypedSPILoader; +import org.apache.shardingsphere.infra.database.core.type.DatabaseType; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +class PrestoDatabaseMetaDataTest { + + private final DialectDatabaseMetaData dialectDatabaseMetaData = DatabaseTypedSPILoader.getService(DialectDatabaseMetaData.class, TypedSPILoader.getService(DatabaseType.class, "Presto")); + + @Test + void assertGetQuoteCharacter() { + assertThat(dialectDatabaseMetaData.getQuoteCharacter(), is(QuoteCharacter.QUOTE)); + } + + @Test + void assertGetDefaultNullsOrderType() { + assertThat(dialectDatabaseMetaData.getDefaultNullsOrderType(), is(NullsOrderType.LOW)); + } +}