From e250f0636e3f42be316cb7ca412f77b8e653cd83 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Tue, 21 Oct 2025 14:55:18 +0300 Subject: [PATCH 01/11] Add FirebirdMetaDataLoader --- .../data/loader/FirebirdMetaDataLoader.java | 77 +++++++++++++++++++ ...metadata.data.loader.DialectMetaDataLoader | 18 +++++ 2 files changed, 95 insertions(+) create mode 100644 database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java create mode 100644 database/connector/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.database.connector.core.metadata.data.loader.DialectMetaDataLoader diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java new file mode 100644 index 0000000000000..5f6746b43993f --- /dev/null +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java @@ -0,0 +1,77 @@ +/* + * 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.database.connector.firebird.metadata.data.loader; + +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.DialectMetaDataLoader; +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderConnection; +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.TableMetaDataLoader; +import org.apache.shardingsphere.database.connector.core.metadata.data.model.SchemaMetaData; +import org.apache.shardingsphere.database.connector.core.metadata.data.model.TableMetaData; +import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Objects; + +/** + * Meta data loader for Firebird. + */ +public final class FirebirdMetaDataLoader implements DialectMetaDataLoader { + + @Override + public Collection load(final MetaDataLoaderMaterial material) throws SQLException { + Collection tableMetaData = new LinkedList<>(); + for (String each : material.getActualTableNames()) { + TableMetaDataLoader.load(material.getDataSource(), each, material.getStorageType()).ifPresent(tableMetaData::add); + } + printColumnSizes(material); + return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaData)); + } + + private void printColumnSizes(final MetaDataLoaderMaterial material) throws SQLException { + if (material.getActualTableNames().isEmpty()) { + return; + } + DatabaseTypeRegistry databaseTypeRegistry = new DatabaseTypeRegistry(material.getStorageType()); + try (MetaDataLoaderConnection connection = new MetaDataLoaderConnection(material.getStorageType(), material.getDataSource().getConnection())) { + for (String each : material.getActualTableNames()) { + String formattedTableName = databaseTypeRegistry.formatIdentifierPattern(each); + try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if (!Objects.equals(formattedTableName, tableName)) { + continue; + } + String columnName = resultSet.getString("COLUMN_NAME"); + int columnSize = resultSet.getInt("COLUMN_SIZE"); + System.out.printf("Firebird column size - table: %s, column: %s, size: %d%n", each, columnName, columnSize); + } + } + } + } + } + + @Override + public String getDatabaseType() { + return "Firebird"; + } +} \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.database.connector.core.metadata.data.loader.DialectMetaDataLoader b/database/connector/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.database.connector.core.metadata.data.loader.DialectMetaDataLoader new file mode 100644 index 0000000000000..9913e407ac46e --- /dev/null +++ b/database/connector/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.database.connector.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.database.connector.firebird.metadata.data.loader.FirebirdMetaDataLoader From 3a94594b57b5ed4af02af64515766cce921d6d09 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Tue, 21 Oct 2025 15:54:42 +0300 Subject: [PATCH 02/11] Add dynamic VarChar --- .../metadata/data/FirebirdSizeRegistry.java | 94 +++++++++++++++++++ .../data/loader/FirebirdMetaDataLoader.java | 46 +++++++-- database/protocol/dialect/firebird/pom.xml | 5 + .../prepare/FirebirdReturnColumnPacket.java | 7 +- ...rebirdPrepareStatementCommandExecutor.java | 15 ++- 5 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java new file mode 100644 index 0000000000000..08d9e02737979 --- /dev/null +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java @@ -0,0 +1,94 @@ +/* + * 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.database.connector.firebird.metadata.data; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.OptionalInt; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Registry for Firebird sizes. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class FirebirdSizeRegistry { + + private static final Map> COLUMN_SIZES = new ConcurrentHashMap<>(); + + /** + * Refresh column sizes for a table. + * + * @param schemaName schema name + * @param tableName table name + * @param columnSizes column sizes map + */ + public static void refreshTable(final String schemaName, final String tableName, final Map columnSizes) { + if (null == tableName) { + return; + } + String tableKey = buildTableKey(schemaName, tableName); + if (columnSizes.isEmpty()) { + COLUMN_SIZES.remove(tableKey); + return; + } + Map normalized = new ConcurrentHashMap<>(columnSizes.size(), 1F); + columnSizes.forEach((column, size) -> { + if (null != column) { + normalized.put(toKey(column), size); + } + }); + if (normalized.isEmpty()) { + COLUMN_SIZES.remove(tableKey); + } else { + COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(normalized)); + } + } + + /** + * Find registered column size. + * + * @param schemaName schema name + * @param tableName table name + * @param columnName column name + * @return column size + */ + public static OptionalInt findColumnSize(final String schemaName, final String tableName, final String columnName) { + if (null == tableName || null == columnName) { + return OptionalInt.empty(); + } + Map tableSizes = COLUMN_SIZES.get(buildTableKey(schemaName, tableName)); + if (null == tableSizes) { + return OptionalInt.empty(); + } + Integer columnSize = tableSizes.get(toKey(columnName)); + return null == columnSize ? OptionalInt.empty() : OptionalInt.of(columnSize); + } + + private static String buildTableKey(final String schemaName, final String tableName) { + String schemaKey = null == schemaName ? "" : toKey(schemaName); + return schemaKey + "." + toKey(tableName); + } + + private static String toKey(final String value) { + return value.toUpperCase(Locale.ENGLISH); + } +} \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java index 5f6746b43993f..c479c3c06fe50 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java @@ -24,12 +24,18 @@ import org.apache.shardingsphere.database.connector.core.metadata.data.model.SchemaMetaData; import org.apache.shardingsphere.database.connector.core.metadata.data.model.TableMetaData; import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; +import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdSizeRegistry; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; import java.util.Objects; /** @@ -37,17 +43,25 @@ */ public final class FirebirdMetaDataLoader implements DialectMetaDataLoader { + private static final Set LENGTH_AWARE_TYPES = new HashSet<>(3, 1F); + + static { + LENGTH_AWARE_TYPES.add("VARYING"); + LENGTH_AWARE_TYPES.add("VARCHAR"); + LENGTH_AWARE_TYPES.add("LEGACY_VARYING"); + } + @Override public Collection load(final MetaDataLoaderMaterial material) throws SQLException { Collection tableMetaData = new LinkedList<>(); for (String each : material.getActualTableNames()) { TableMetaDataLoader.load(material.getDataSource(), each, material.getStorageType()).ifPresent(tableMetaData::add); } - printColumnSizes(material); + loadColumnSizes(material); return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaData)); } - private void printColumnSizes(final MetaDataLoaderMaterial material) throws SQLException { + private void loadColumnSizes(final MetaDataLoaderMaterial material) throws SQLException { if (material.getActualTableNames().isEmpty()) { return; } @@ -55,23 +69,41 @@ private void printColumnSizes(final MetaDataLoaderMaterial material) throws SQLE try (MetaDataLoaderConnection connection = new MetaDataLoaderConnection(material.getStorageType(), material.getDataSource().getConnection())) { for (String each : material.getActualTableNames()) { String formattedTableName = databaseTypeRegistry.formatIdentifierPattern(each); + Map columnSizes = new HashMap<>(); try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { while (resultSet.next()) { - String tableName = resultSet.getString("TABLE_NAME"); - if (!Objects.equals(formattedTableName, tableName)) { + if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { + continue; + } + if (!isLengthAwareType(resultSet.getString("TYPE_NAME"))) { continue; } String columnName = resultSet.getString("COLUMN_NAME"); - int columnSize = resultSet.getInt("COLUMN_SIZE"); - System.out.printf("Firebird column size - table: %s, column: %s, size: %d%n", each, columnName, columnSize); + if (null != columnName) { + columnSizes.put(columnName, resultSet.getInt("COLUMN_SIZE")); + } } } + FirebirdSizeRegistry.refreshTable(material.getDefaultSchemaName(), each, columnSizes); + } + } + } + + private boolean isLengthAwareType(final String typeName) { + if (null == typeName) { + return false; + } + String normalized = typeName.toUpperCase(Locale.ENGLISH); + for (String each : LENGTH_AWARE_TYPES) { + if (normalized.startsWith(each)) { + return true; } } + return false; } @Override public String getDatabaseType() { return "Firebird"; } -} \ No newline at end of file +} diff --git a/database/protocol/dialect/firebird/pom.xml b/database/protocol/dialect/firebird/pom.xml index 7342bc649ff44..73ebedba86789 100644 --- a/database/protocol/dialect/firebird/pom.xml +++ b/database/protocol/dialect/firebird/pom.xml @@ -32,6 +32,11 @@ shardingsphere-database-protocol-core ${project.version} + + org.apache.shardingsphere + shardingsphere-database-connector-firebird + ${project.version} + org.apache.shardingsphere shardingsphere-database-exception-core diff --git a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java index 1265f2f73ff1f..3248ad890f806 100644 --- a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java +++ b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java @@ -47,6 +47,8 @@ public final class FirebirdReturnColumnPacket extends FirebirdPacket { private final String columnAlias; private final String owner; + + private final Integer columnLength; @Override protected void write(final FirebirdPacketPayload payload) { @@ -65,7 +67,10 @@ protected void write(final FirebirdPacketPayload payload) { FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.SCALE, 0, payload); break; case LENGTH: - FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.LENGTH, FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()).getLength(), payload); + int length = null != columnLength + ? columnLength + : FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()).getLength(); + FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.LENGTH, length, payload); break; case FIELD: FirebirdPrepareStatementReturnPacket.writeString(FirebirdSQLInfoPacketType.FIELD, column.getName(), payload); diff --git a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java index ad8455667447c..1c9c3852d98a3 100644 --- a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java +++ b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import org.apache.shardingsphere.database.connector.core.type.DatabaseType; import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; +import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdSizeRegistry; import org.apache.shardingsphere.database.protocol.firebird.exception.FirebirdProtocolException; import org.apache.shardingsphere.database.protocol.firebird.packet.command.query.info.type.sql.FirebirdSQLInfoPacketType; import org.apache.shardingsphere.database.protocol.firebird.packet.command.query.info.type.sql.FirebirdSQLInfoReturnValue; @@ -84,10 +85,7 @@ import java.sql.SQLException; import java.sql.Types; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.Map; +import java.util.*; /** * Firebird prepare transaction command executor. @@ -457,6 +455,13 @@ private void processColumn(final Collection describe String tableAliasString = null == tableAlias ? table.getName() : tableAlias.getValue(); String columnAliasString = null == columnAlias ? column.getName() : columnAlias.getValue(); String owner = connectionSession.getConnectionContext().getGrantee().getUsername(); - describeColumns.add(new FirebirdReturnColumnPacket(requestedItems, idx, table, column, tableAliasString, columnAliasString, owner)); + Integer columnLength = null; + if (null != table && null != column) { + OptionalInt columnSize = FirebirdSizeRegistry.findColumnSize(connectionSession.getCurrentDatabaseName(), table.getName(), column.getName()); + if (columnSize.isPresent()) { + columnLength = columnSize.getAsInt(); + } + } + describeColumns.add(new FirebirdReturnColumnPacket(requestedItems, idx, table, column, tableAliasString, columnAliasString, owner, columnLength)); } } From 477d7b6845d7a145cc8015acd6d56263495adf47 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Tue, 21 Oct 2025 17:24:57 +0300 Subject: [PATCH 03/11] refactor --- .../data/FirebirdLengthAwareTypes.java | 54 +++++++++++++ .../metadata/data/FirebirdSizeRegistry.java | 17 +--- .../data/loader/FirebirdColumnSizeLoader.java | 78 +++++++++++++++++++ .../data/loader/FirebirdMetaDataLoader.java | 55 ++----------- ...rebirdPrepareStatementCommandExecutor.java | 16 ++-- 5 files changed, 148 insertions(+), 72 deletions(-) create mode 100644 database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java create mode 100644 database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java new file mode 100644 index 0000000000000..a8a6d940184cd --- /dev/null +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java @@ -0,0 +1,54 @@ +/* + * 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.database.connector.firebird.metadata.data; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + * Firebird types that carry dynamic length information. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class FirebirdLengthAwareTypes { + + private static final Set LENGTH_AWARE_TYPES = new HashSet<>(Arrays.asList("VARYING", "VARCHAR", "LEGACY_VARYING")); + + /** + * Check whether provided type requires length information. + * + * @param typeName JDBC type name + * @return {@code true} if provided type expects length + */ + public static boolean matches(final String typeName) { + if (null == typeName) { + return false; + } + String normalized = typeName.toUpperCase(Locale.ENGLISH); + for (String each : LENGTH_AWARE_TYPES) { + if (normalized.startsWith(each)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java index 08d9e02737979..14381a8c7d1ee 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java @@ -20,10 +20,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.Collections; -import java.util.Locale; -import java.util.Map; -import java.util.OptionalInt; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -50,17 +47,7 @@ public static void refreshTable(final String schemaName, final String tableName, COLUMN_SIZES.remove(tableKey); return; } - Map normalized = new ConcurrentHashMap<>(columnSizes.size(), 1F); - columnSizes.forEach((column, size) -> { - if (null != column) { - normalized.put(toKey(column), size); - } - }); - if (normalized.isEmpty()) { - COLUMN_SIZES.remove(tableKey); - } else { - COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(normalized)); - } + COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(new HashMap<>(columnSizes))); } /** diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java new file mode 100644 index 0000000000000..cb9b639d2cb0f --- /dev/null +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -0,0 +1,78 @@ +/* + * 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.database.connector.firebird.metadata.data.loader; + +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderConnection; +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; +import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; +import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdLengthAwareTypes; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * Loader for Firebird column sizes. + */ +final class FirebirdColumnSizeLoader { + + private final MetaDataLoaderMaterial material; + + FirebirdColumnSizeLoader(final MetaDataLoaderMaterial material) { + this.material = material; + } + + Map> load() throws SQLException { + if (material.getActualTableNames().isEmpty()) { + return Collections.emptyMap(); + } + Map> result = new HashMap<>(material.getActualTableNames().size(), 1F); + DatabaseTypeRegistry databaseTypeRegistry = new DatabaseTypeRegistry(material.getStorageType()); + try (MetaDataLoaderConnection connection = new MetaDataLoaderConnection(material.getStorageType(), material.getDataSource().getConnection())) { + for (String each : material.getActualTableNames()) { + String formattedTableName = databaseTypeRegistry.formatIdentifierPattern(each); + Map columnSizes = loadTableColumnSizes(connection, formattedTableName); + result.put(each, columnSizes); + } + } + return result; + } + + private Map loadTableColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName) throws SQLException { + Map result = new HashMap<>(); + try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { + while (resultSet.next()) { + if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { + continue; + } + if (!FirebirdLengthAwareTypes.matches(resultSet.getString("TYPE_NAME"))) { + continue; + } + String columnName = resultSet.getString("COLUMN_NAME"); + if (null != columnName) { + result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); + } + } + } + return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); + } +} \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java index c479c3c06fe50..2229c10fdb629 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java @@ -43,63 +43,18 @@ */ public final class FirebirdMetaDataLoader implements DialectMetaDataLoader { - private static final Set LENGTH_AWARE_TYPES = new HashSet<>(3, 1F); - - static { - LENGTH_AWARE_TYPES.add("VARYING"); - LENGTH_AWARE_TYPES.add("VARCHAR"); - LENGTH_AWARE_TYPES.add("LEGACY_VARYING"); - } - @Override public Collection load(final MetaDataLoaderMaterial material) throws SQLException { Collection tableMetaData = new LinkedList<>(); for (String each : material.getActualTableNames()) { TableMetaDataLoader.load(material.getDataSource(), each, material.getStorageType()).ifPresent(tableMetaData::add); } - loadColumnSizes(material); - return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaData)); - } - - private void loadColumnSizes(final MetaDataLoaderMaterial material) throws SQLException { - if (material.getActualTableNames().isEmpty()) { - return; - } - DatabaseTypeRegistry databaseTypeRegistry = new DatabaseTypeRegistry(material.getStorageType()); - try (MetaDataLoaderConnection connection = new MetaDataLoaderConnection(material.getStorageType(), material.getDataSource().getConnection())) { - for (String each : material.getActualTableNames()) { - String formattedTableName = databaseTypeRegistry.formatIdentifierPattern(each); - Map columnSizes = new HashMap<>(); - try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { - while (resultSet.next()) { - if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { - continue; - } - if (!isLengthAwareType(resultSet.getString("TYPE_NAME"))) { - continue; - } - String columnName = resultSet.getString("COLUMN_NAME"); - if (null != columnName) { - columnSizes.put(columnName, resultSet.getInt("COLUMN_SIZE")); - } - } - } - FirebirdSizeRegistry.refreshTable(material.getDefaultSchemaName(), each, columnSizes); - } - } - } - - private boolean isLengthAwareType(final String typeName) { - if (null == typeName) { - return false; - } - String normalized = typeName.toUpperCase(Locale.ENGLISH); - for (String each : LENGTH_AWARE_TYPES) { - if (normalized.startsWith(each)) { - return true; - } + Map> columnSizes = new FirebirdColumnSizeLoader(material).load(); + for (String each : material.getActualTableNames()) { + Map tableSizes = columnSizes.getOrDefault(each, Collections.emptyMap()); + FirebirdSizeRegistry.refreshTable(material.getDefaultSchemaName(), each, tableSizes); } - return false; + return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaData)); } @Override diff --git a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java index 1c9c3852d98a3..d0bd46fafdff5 100644 --- a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java +++ b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java @@ -455,13 +455,15 @@ private void processColumn(final Collection describe String tableAliasString = null == tableAlias ? table.getName() : tableAlias.getValue(); String columnAliasString = null == columnAlias ? column.getName() : columnAlias.getValue(); String owner = connectionSession.getConnectionContext().getGrantee().getUsername(); - Integer columnLength = null; - if (null != table && null != column) { - OptionalInt columnSize = FirebirdSizeRegistry.findColumnSize(connectionSession.getCurrentDatabaseName(), table.getName(), column.getName()); - if (columnSize.isPresent()) { - columnLength = columnSize.getAsInt(); - } - } + Integer columnLength = resolveColumnLength(table, column); describeColumns.add(new FirebirdReturnColumnPacket(requestedItems, idx, table, column, tableAliasString, columnAliasString, owner, columnLength)); } + + private Integer resolveColumnLength(final ShardingSphereTable table, final ShardingSphereColumn column) { + if (null == table || null == column) { + return null; + } + OptionalInt columnSize = FirebirdSizeRegistry.findColumnSize(connectionSession.getCurrentDatabaseName(), table.getName(), column.getName()); + return columnSize.isPresent() ? columnSize.getAsInt() : null; + } } From a51847534a11e56578807a9e560ce7f225988d65 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Tue, 21 Oct 2025 17:36:55 +0300 Subject: [PATCH 04/11] delete FirebirdLengthAwareTypes --- .../data/FirebirdLengthAwareTypes.java | 54 ------------------- .../data/loader/FirebirdColumnSizeLoader.java | 11 +++- .../data/loader/FirebirdMetaDataLoader.java | 8 --- 3 files changed, 9 insertions(+), 64 deletions(-) delete mode 100644 database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java deleted file mode 100644 index a8a6d940184cd..0000000000000 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdLengthAwareTypes.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.database.connector.firebird.metadata.data; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; - -/** - * Firebird types that carry dynamic length information. - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class FirebirdLengthAwareTypes { - - private static final Set LENGTH_AWARE_TYPES = new HashSet<>(Arrays.asList("VARYING", "VARCHAR", "LEGACY_VARYING")); - - /** - * Check whether provided type requires length information. - * - * @param typeName JDBC type name - * @return {@code true} if provided type expects length - */ - public static boolean matches(final String typeName) { - if (null == typeName) { - return false; - } - String normalized = typeName.toUpperCase(Locale.ENGLISH); - for (String each : LENGTH_AWARE_TYPES) { - if (normalized.startsWith(each)) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index cb9b639d2cb0f..a5442a02a963d 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -20,7 +20,6 @@ import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderConnection; import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; -import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdLengthAwareTypes; import java.sql.ResultSet; import java.sql.SQLException; @@ -64,7 +63,7 @@ private Map loadTableColumnSizes(final MetaDataLoaderConnection if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - if (!FirebirdLengthAwareTypes.matches(resultSet.getString("TYPE_NAME"))) { + if (!isLengthAwareType(resultSet.getString("TYPE_NAME"))) { continue; } String columnName = resultSet.getString("COLUMN_NAME"); @@ -75,4 +74,12 @@ private Map loadTableColumnSizes(final MetaDataLoaderConnection } return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); } + + private boolean isLengthAwareType(final String typeName) { + if (null == typeName) { + return false; + } + String normalized = typeName.toUpperCase(Locale.ENGLISH); + return normalized.startsWith("VARCHAR") || normalized.startsWith("VARYING") || normalized.startsWith("LEGACY_VARYING"); + } } \ No newline at end of file diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java index 2229c10fdb629..eafa928767765 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java @@ -18,25 +18,17 @@ package org.apache.shardingsphere.database.connector.firebird.metadata.data.loader; import org.apache.shardingsphere.database.connector.core.metadata.data.loader.DialectMetaDataLoader; -import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderConnection; import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; import org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.TableMetaDataLoader; import org.apache.shardingsphere.database.connector.core.metadata.data.model.SchemaMetaData; import org.apache.shardingsphere.database.connector.core.metadata.data.model.TableMetaData; -import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdSizeRegistry; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; -import java.util.Locale; import java.util.Map; -import java.util.HashMap; -import java.util.Set; -import java.util.HashSet; -import java.util.Objects; /** * Meta data loader for Firebird. From a5196aa75a47248233f6e681d93e23595b50ca30 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Wed, 22 Oct 2025 12:30:29 +0300 Subject: [PATCH 05/11] correct Column Type Names --- .../metadata/data/loader/FirebirdColumnSizeLoader.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index a5442a02a963d..db5a642b366a3 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -63,7 +63,7 @@ private Map loadTableColumnSizes(final MetaDataLoaderConnection if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - if (!isLengthAwareType(resultSet.getString("TYPE_NAME"))) { + if (!(resultSet.getString("TYPE_NAME").toUpperCase(Locale.ENGLISH).startsWith("VARCHAR"))) { continue; } String columnName = resultSet.getString("COLUMN_NAME"); @@ -74,12 +74,4 @@ private Map loadTableColumnSizes(final MetaDataLoaderConnection } return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); } - - private boolean isLengthAwareType(final String typeName) { - if (null == typeName) { - return false; - } - String normalized = typeName.toUpperCase(Locale.ENGLISH); - return normalized.startsWith("VARCHAR") || normalized.startsWith("VARYING") || normalized.startsWith("LEGACY_VARYING"); - } } \ No newline at end of file From e11a54cad8ab3a947fbe34bf97d15b98fa9e6508 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Wed, 22 Oct 2025 13:25:40 +0300 Subject: [PATCH 06/11] refactor --- .../metadata/data/FirebirdSizeRegistry.java | 19 +++++++++++++++++-- .../data/loader/FirebirdColumnSizeLoader.java | 3 ++- ...rebirdPrepareStatementCommandExecutor.java | 6 +++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java index 14381a8c7d1ee..bc4658ec097ad 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java @@ -20,7 +20,11 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.OptionalInt; import java.util.concurrent.ConcurrentHashMap; /** @@ -47,7 +51,18 @@ public static void refreshTable(final String schemaName, final String tableName, COLUMN_SIZES.remove(tableKey); return; } - COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(new HashMap<>(columnSizes))); + Map normalizedColumnSizes = new HashMap<>(columnSizes.size(), 1F); + for (Map.Entry entry : columnSizes.entrySet()) { + if (null == entry.getKey()) { + continue; + } + normalizedColumnSizes.put(toKey(entry.getKey()), entry.getValue()); + } + if (normalizedColumnSizes.isEmpty()) { + COLUMN_SIZES.remove(tableKey); + return; + } + COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(normalizedColumnSizes)); } /** diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index db5a642b366a3..ca7b967ae30b4 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -63,7 +63,8 @@ private Map loadTableColumnSizes(final MetaDataLoaderConnection if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - if (!(resultSet.getString("TYPE_NAME").toUpperCase(Locale.ENGLISH).startsWith("VARCHAR"))) { + String typeName = resultSet.getString("TYPE_NAME"); + if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { continue; } String columnName = resultSet.getString("COLUMN_NAME"); diff --git a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java index d0bd46fafdff5..72fd3a104da8e 100644 --- a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java +++ b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java @@ -85,7 +85,11 @@ import java.sql.SQLException; import java.sql.Types; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Map; +import java.util.OptionalInt; /** * Firebird prepare transaction command executor. From 24babbfe085b270661c8fe8e6d560ddba68711d2 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Wed, 22 Oct 2025 14:56:21 +0300 Subject: [PATCH 07/11] add Firebird BLOB segment size loader --- .../data/loader/FirebirdColumnSizeLoader.java | 49 ++++++++++++++++--- .../prepare/FirebirdReturnColumnPacket.java | 10 ++-- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index ca7b967ae30b4..4c376f9abac04 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -21,6 +21,7 @@ import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collections; @@ -34,6 +35,14 @@ */ final class FirebirdColumnSizeLoader { + + private static final String LOAD_BLOB_SEGMENT_SIZES_SQL = "SELECT TRIM(rf.RDB$FIELD_NAME) AS COLUMN_NAME, " + + "COALESCE(f.RDB$SEGMENT_LENGTH, 0) AS SEGMENT_SIZE " + + "FROM RDB$RELATION_FIELDS rf " + + "JOIN RDB$FIELDS f ON rf.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME " + + "WHERE TRIM(UPPER(rf.RDB$RELATION_NAME)) = ? " + + "AND f.RDB$FIELD_TYPE = 261"; + private final MetaDataLoaderMaterial material; FirebirdColumnSizeLoader(final MetaDataLoaderMaterial material) { @@ -58,21 +67,45 @@ Map> load() throws SQLException { private Map loadTableColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName) throws SQLException { Map result = new HashMap<>(); + loadVarcharColumnSizes(connection, formattedTableName, result); + loadBlobSegmentSizes(connection, formattedTableName, result); + return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); + } + + private void loadVarcharColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { while (resultSet.next()) { if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - String typeName = resultSet.getString("TYPE_NAME"); - if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { - continue; - } - String columnName = resultSet.getString("COLUMN_NAME"); - if (null != columnName) { - result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); + String typeName = resultSet.getString("TYPE_NAME"); + if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { + continue; + } + String columnName = resultSet.getString("COLUMN_NAME"); + if (null != columnName) { + result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); + } + } + } + } + + private void loadBlobSegmentSizes(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { + try (PreparedStatement preparedStatement = connection.prepareStatement(LOAD_BLOB_SEGMENT_SIZES_SQL)) { + preparedStatement.setString(1, formattedTableName.toUpperCase(Locale.ENGLISH)); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + String columnName = resultSet.getString("COLUMN_NAME"); + if (null == columnName) { + continue; + } + String trimmedColumnName = columnName.trim(); + if (trimmedColumnName.isEmpty()) { + continue; + } + result.put(trimmedColumnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("SEGMENT_SIZE")); } } } - return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); } } \ No newline at end of file diff --git a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java index 3248ad890f806..f638442e25ada 100644 --- a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java +++ b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java @@ -52,25 +52,23 @@ public final class FirebirdReturnColumnPacket extends FirebirdPacket { @Override protected void write(final FirebirdPacketPayload payload) { + FirebirdBinaryColumnType columnType = FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()); for (FirebirdSQLInfoPacketType requestedItem : requestedItems) { switch (requestedItem) { case SQLDA_SEQ: FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.SQLDA_SEQ, index, payload); break; case TYPE: - FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.TYPE, FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()).getValue() + 1, payload); + FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.TYPE, columnType.getValue() + 1, payload); break; case SUB_TYPE: - FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.SUB_TYPE, FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()).getSubtype(), payload); + FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.SUB_TYPE, columnType.getSubtype(), payload); break; case SCALE: FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.SCALE, 0, payload); break; case LENGTH: - int length = null != columnLength - ? columnLength - : FirebirdBinaryColumnType.valueOfJDBCType(column.getDataType()).getLength(); - FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.LENGTH, length, payload); + FirebirdPrepareStatementReturnPacket.writeInt(FirebirdSQLInfoPacketType.LENGTH, null != columnLength ? columnLength : columnType.getLength(), payload); break; case FIELD: FirebirdPrepareStatementReturnPacket.writeString(FirebirdSQLInfoPacketType.FIELD, column.getName(), payload); From 0a19a517644867b6f879bfff1c82cada9ce1c8d0 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Wed, 22 Oct 2025 15:02:30 +0300 Subject: [PATCH 08/11] spotless fix --- .../metadata/data/FirebirdSizeRegistry.java | 12 +++---- .../data/loader/FirebirdColumnSizeLoader.java | 33 +++++++++---------- .../data/loader/FirebirdMetaDataLoader.java | 4 +-- .../prepare/FirebirdReturnColumnPacket.java | 2 +- ...rebirdPrepareStatementCommandExecutor.java | 2 +- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java index bc4658ec097ad..010c5a3c4370a 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistry.java @@ -32,9 +32,9 @@ */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class FirebirdSizeRegistry { - + private static final Map> COLUMN_SIZES = new ConcurrentHashMap<>(); - + /** * Refresh column sizes for a table. * @@ -64,7 +64,7 @@ public static void refreshTable(final String schemaName, final String tableName, } COLUMN_SIZES.put(tableKey, Collections.unmodifiableMap(normalizedColumnSizes)); } - + /** * Find registered column size. * @@ -84,13 +84,13 @@ public static OptionalInt findColumnSize(final String schemaName, final String t Integer columnSize = tableSizes.get(toKey(columnName)); return null == columnSize ? OptionalInt.empty() : OptionalInt.of(columnSize); } - + private static String buildTableKey(final String schemaName, final String tableName) { String schemaKey = null == schemaName ? "" : toKey(schemaName); return schemaKey + "." + toKey(tableName); } - + private static String toKey(final String value) { return value.toUpperCase(Locale.ENGLISH); } -} \ No newline at end of file +} diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index 4c376f9abac04..a958022c91242 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -34,21 +34,20 @@ * Loader for Firebird column sizes. */ final class FirebirdColumnSizeLoader { - - + private static final String LOAD_BLOB_SEGMENT_SIZES_SQL = "SELECT TRIM(rf.RDB$FIELD_NAME) AS COLUMN_NAME, " + "COALESCE(f.RDB$SEGMENT_LENGTH, 0) AS SEGMENT_SIZE " + "FROM RDB$RELATION_FIELDS rf " + "JOIN RDB$FIELDS f ON rf.RDB$FIELD_SOURCE = f.RDB$FIELD_NAME " + "WHERE TRIM(UPPER(rf.RDB$RELATION_NAME)) = ? " + "AND f.RDB$FIELD_TYPE = 261"; - + private final MetaDataLoaderMaterial material; - + FirebirdColumnSizeLoader(final MetaDataLoaderMaterial material) { this.material = material; } - + Map> load() throws SQLException { if (material.getActualTableNames().isEmpty()) { return Collections.emptyMap(); @@ -64,32 +63,32 @@ Map> load() throws SQLException { } return result; } - + private Map loadTableColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName) throws SQLException { Map result = new HashMap<>(); loadVarcharColumnSizes(connection, formattedTableName, result); loadBlobSegmentSizes(connection, formattedTableName, result); return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); } - + private void loadVarcharColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { while (resultSet.next()) { if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - String typeName = resultSet.getString("TYPE_NAME"); - if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { - continue; - } - String columnName = resultSet.getString("COLUMN_NAME"); - if (null != columnName) { - result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); - } + String typeName = resultSet.getString("TYPE_NAME"); + if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { + continue; + } + String columnName = resultSet.getString("COLUMN_NAME"); + if (null != columnName) { + result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); + } } } } - + private void loadBlobSegmentSizes(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { try (PreparedStatement preparedStatement = connection.prepareStatement(LOAD_BLOB_SEGMENT_SIZES_SQL)) { preparedStatement.setString(1, formattedTableName.toUpperCase(Locale.ENGLISH)); @@ -108,4 +107,4 @@ private void loadBlobSegmentSizes(final MetaDataLoaderConnection connection, fin } } } -} \ No newline at end of file +} diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java index eafa928767765..c90a8a96cf0c1 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoader.java @@ -34,7 +34,7 @@ * Meta data loader for Firebird. */ public final class FirebirdMetaDataLoader implements DialectMetaDataLoader { - + @Override public Collection load(final MetaDataLoaderMaterial material) throws SQLException { Collection tableMetaData = new LinkedList<>(); @@ -48,7 +48,7 @@ public Collection load(final MetaDataLoaderMaterial material) th } return Collections.singleton(new SchemaMetaData(material.getDefaultSchemaName(), tableMetaData)); } - + @Override public String getDatabaseType() { return "Firebird"; diff --git a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java index f638442e25ada..ea0f8ebfb99a6 100644 --- a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java +++ b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacket.java @@ -47,7 +47,7 @@ public final class FirebirdReturnColumnPacket extends FirebirdPacket { private final String columnAlias; private final String owner; - + private final Integer columnLength; @Override diff --git a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java index 72fd3a104da8e..1377cbd8100b2 100644 --- a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java +++ b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutor.java @@ -462,7 +462,7 @@ private void processColumn(final Collection describe Integer columnLength = resolveColumnLength(table, column); describeColumns.add(new FirebirdReturnColumnPacket(requestedItems, idx, table, column, tableAliasString, columnAliasString, owner, columnLength)); } - + private Integer resolveColumnLength(final ShardingSphereTable table, final ShardingSphereColumn column) { if (null == table || null == column) { return null; From d940c054382fba99acb708dbb4256a8688335013 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Thu, 23 Oct 2025 10:47:14 +0300 Subject: [PATCH 09/11] refactor FirebirdBinaryColumnType to represent dynamic types with zero length --- .../packet/command/query/FirebirdBinaryColumnType.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/FirebirdBinaryColumnType.java b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/FirebirdBinaryColumnType.java index e5db410148011..3d42ecf0507bb 100644 --- a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/FirebirdBinaryColumnType.java +++ b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/FirebirdBinaryColumnType.java @@ -34,18 +34,19 @@ @Getter public enum FirebirdBinaryColumnType implements BinaryColumnType { - // TODO add different varying length based on a row length + // TODO add support for retrieving length for ARRAY types. + // Currently not possible due to existing issues with ARRAY handling in the database itself. TEXT(452, 255), - VARYING(448, 255), + VARYING(448, 0), LEGACY_TEXT(452, 255), - LEGACY_VARYING(448, 255), + LEGACY_VARYING(448, 0), SHORT(500, 2), LONG(496, 4), FLOAT(482, 4), DOUBLE(480, 8), D_FLOAT(530, 8), TIMESTAMP(510, 8), - BLOB(520, 8), + BLOB(520, 0), ARRAY(540, 255), QUAD(550, 4), TIME(560, 4), From e17c963bba65fc7f8ac4f3f45e30000a871c66aa Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Thu, 23 Oct 2025 13:04:12 +0300 Subject: [PATCH 10/11] add new tests and fix existing ones --- .../data/FirebirdSizeRegistryTest.java | 71 ++++++++++++ .../loader/FirebirdColumnSizeLoaderTest.java | 102 ++++++++++++++++++ .../loader/FirebirdMetaDataLoaderTest.java | 74 +++++++++++++ ...ebirdPrepareStatementReturnPacketTest.java | 2 +- .../FirebirdReturnColumnPacketTest.java | 12 ++- 5 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistryTest.java create mode 100644 database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java create mode 100644 database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoaderTest.java diff --git a/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistryTest.java b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistryTest.java new file mode 100644 index 0000000000000..35f49d5de234e --- /dev/null +++ b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/FirebirdSizeRegistryTest.java @@ -0,0 +1,71 @@ +/* + * 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.database.connector.firebird.metadata.data; + +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalInt; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FirebirdSizeRegistryTest { + + @Test + void assertRefreshAndFindColumnSize() { + Map columnSizes = Collections.singletonMap("varchar_col", 64); + FirebirdSizeRegistry.refreshTable("schema_a", "table_a", columnSizes); + OptionalInt actual = FirebirdSizeRegistry.findColumnSize("schema_a", "table_a", "VARCHAR_COL"); + assertTrue(actual.isPresent()); + assertThat(actual.getAsInt(), is(64)); + FirebirdSizeRegistry.refreshTable("schema_a", "table_a", Collections.emptyMap()); + } + + @Test + void assertRefreshTableRemovesEntryWhenEmptyColumnSizesProvided() { + FirebirdSizeRegistry.refreshTable("schema_b", "table_b", Collections.singletonMap("col", 32)); + FirebirdSizeRegistry.refreshTable("schema_b", "table_b", Collections.emptyMap()); + assertFalse(FirebirdSizeRegistry.findColumnSize("schema_b", "table_b", "COL").isPresent()); + } + + @Test + void assertRefreshTableSkipsNullColumnNames() { + Map columnSizes = new HashMap<>(2, 1F); + columnSizes.put("valid", 12); + columnSizes.put(null, 24); + FirebirdSizeRegistry.refreshTable("schema_c", "table_c", columnSizes); + OptionalInt actual = FirebirdSizeRegistry.findColumnSize("schema_c", "table_c", "VaLiD"); + assertTrue(actual.isPresent()); + assertThat(actual.getAsInt(), is(12)); + assertFalse(FirebirdSizeRegistry.findColumnSize("schema_c", "table_c", null).isPresent()); + FirebirdSizeRegistry.refreshTable("schema_c", "table_c", Collections.emptyMap()); + } + + @Test + void assertRefreshTableRemovesWhenAllColumnsInvalid() { + Map columnSizes = new HashMap<>(1, 1F); + columnSizes.put(null, 48); + FirebirdSizeRegistry.refreshTable("schema_d", "table_d", columnSizes); + assertFalse(FirebirdSizeRegistry.findColumnSize("schema_d", "table_d", "any").isPresent()); + } +} diff --git a/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java new file mode 100644 index 0000000000000..1e10d74d389f5 --- /dev/null +++ b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java @@ -0,0 +1,102 @@ +/* + * 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.database.connector.firebird.metadata.data.loader; + +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; +import org.apache.shardingsphere.database.connector.core.type.DatabaseType; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; +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.junit.jupiter.MockitoExtension; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasKey; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class FirebirdColumnSizeLoaderTest { + + private static final Collection TABLES = Collections.singleton("test_table"); + + @Mock + private DataSource dataSource; + + @Mock + private Connection connection; + + @Mock + private DatabaseMetaData databaseMetaData; + + @Mock + private ResultSet columnsResultSet; + + @Mock + private PreparedStatement preparedStatement; + + @Mock + private ResultSet blobResultSet; + + private MetaDataLoaderMaterial material; + + @BeforeEach + void setUp() throws SQLException { + DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "Firebird"); + material = new MetaDataLoaderMaterial(TABLES, "logic_ds", dataSource, databaseType, "schema"); + when(dataSource.getConnection()).thenReturn(connection); + when(connection.getMetaData()).thenReturn(databaseMetaData); + when(connection.getCatalog()).thenReturn("catalog"); + when(connection.getSchema()).thenReturn("schema"); + when(connection.prepareStatement(anyString())).thenReturn(preparedStatement); + when(preparedStatement.executeQuery()).thenReturn(blobResultSet); + when(databaseMetaData.getColumns("catalog", "schema", "TEST_TABLE", "%")).thenReturn(columnsResultSet); + } + + @Test + void assertLoadReturnsCombinedVarcharAndBlobSizes() throws SQLException { + when(columnsResultSet.next()).thenReturn(true, true, false); + when(columnsResultSet.getString("TABLE_NAME")).thenReturn("TEST_TABLE", "TEST_TABLE"); + when(columnsResultSet.getString("TYPE_NAME")).thenReturn("varchar", "integer"); + when(columnsResultSet.getString("COLUMN_NAME")).thenReturn("varchar_col", "ignored_col"); + when(columnsResultSet.getInt("COLUMN_SIZE")).thenReturn(128, 256); + when(blobResultSet.next()).thenReturn(true, true, false); + when(blobResultSet.getString("COLUMN_NAME")).thenReturn(" blob_col ", " "); + when(blobResultSet.getInt("SEGMENT_SIZE")).thenReturn(2048, 4096); + Map> actual = new FirebirdColumnSizeLoader(material).load(); + assertThat(actual, hasKey("test_table")); + Map tableSizes = actual.get("test_table"); + assertThat(tableSizes.size(), is(2)); + assertThat(tableSizes.get("VARCHAR_COL"), is(128)); + assertThat(tableSizes.get("BLOB_COL"), is(2048)); + verify(preparedStatement).setString(1, "TEST_TABLE"); + } +} diff --git a/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoaderTest.java b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoaderTest.java new file mode 100644 index 0000000000000..2f2793acc34b9 --- /dev/null +++ b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdMetaDataLoaderTest.java @@ -0,0 +1,74 @@ +/* + * 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.database.connector.firebird.metadata.data.loader; + +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.MetaDataLoaderMaterial; +import org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.TableMetaDataLoader; +import org.apache.shardingsphere.database.connector.core.metadata.data.model.SchemaMetaData; +import org.apache.shardingsphere.database.connector.core.metadata.data.model.TableMetaData; +import org.apache.shardingsphere.database.connector.core.type.DatabaseType; +import org.apache.shardingsphere.database.connector.firebird.metadata.data.FirebirdSizeRegistry; +import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class FirebirdMetaDataLoaderTest { + + @Test + void assertLoadRefreshesSizeRegistry() throws SQLException { + DataSource dataSource = mock(DataSource.class); + DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "Firebird"); + MetaDataLoaderMaterial material = new MetaDataLoaderMaterial(Collections.singleton("test_table"), "logic_ds", dataSource, + databaseType, "schema"); + TableMetaData tableMetaData = new TableMetaData("test_table", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + Map tableSizes = Collections.singletonMap("COLUMN", 16); + Map> allSizes = Collections.singletonMap("test_table", tableSizes); + try ( + MockedStatic tableLoaderMocked = mockStatic(TableMetaDataLoader.class); + MockedStatic sizeRegistryMocked = mockStatic(FirebirdSizeRegistry.class); + MockedConstruction columnSizeLoaderMocked = mockConstruction(FirebirdColumnSizeLoader.class, + (mock, context) -> when(mock.load()).thenReturn(allSizes))) { + tableLoaderMocked.when(() -> TableMetaDataLoader.load(dataSource, "test_table", databaseType)).thenReturn(Optional.of(tableMetaData)); + Collection actual = new FirebirdMetaDataLoader().load(material); + assertThat(actual, hasSize(1)); + SchemaMetaData schema = actual.iterator().next(); + assertThat(schema.getName(), is("schema")); + assertThat(schema.getTables(), contains(tableMetaData)); + sizeRegistryMocked.verify(() -> FirebirdSizeRegistry.refreshTable("schema", "test_table", tableSizes)); + verify(columnSizeLoaderMocked.constructed().get(0)).load(); + } + } +} diff --git a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdPrepareStatementReturnPacketTest.java b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdPrepareStatementReturnPacketTest.java index 403c10516e47e..730c5b52dddb7 100644 --- a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdPrepareStatementReturnPacketTest.java +++ b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdPrepareStatementReturnPacketTest.java @@ -46,7 +46,7 @@ void assertWrite() { ShardingSphereColumn column = new ShardingSphereColumn("col", Types.INTEGER, false, false, false, true, false, true); ShardingSphereTable table = new ShardingSphereTable("tbl", Collections.singleton(column), Collections.emptyList(), Collections.emptyList()); FirebirdReturnColumnPacket columnPacket = - new FirebirdReturnColumnPacket(Collections.singleton(FirebirdSQLInfoPacketType.DESCRIBE_END), 1, table, column, "", "", ""); + new FirebirdReturnColumnPacket(Collections.singleton(FirebirdSQLInfoPacketType.DESCRIBE_END), 1, table, column, "", "", "", null); packet.getDescribeSelect().add(columnPacket); packet.getDescribeBind().add(columnPacket); packet.write(payload); diff --git a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java index aba3b5a2f72d5..ee42eb9b57921 100644 --- a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java +++ b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java @@ -54,10 +54,20 @@ void assertWrite() { FirebirdSQLInfoPacketType.RELATION, FirebirdSQLInfoPacketType.RELATION_ALIAS, FirebirdSQLInfoPacketType.OWNER, - FirebirdSQLInfoPacketType.DESCRIBE_END), 1, table, column, "t", "c", "o"); + FirebirdSQLInfoPacketType.DESCRIBE_END), 1, table, column, "t", "c", "o", 99); when(payload.getCharset()).thenReturn(java.nio.charset.StandardCharsets.UTF_8); packet.write(payload); verify(payload).writeInt1(FirebirdSQLInfoPacketType.SQLDA_SEQ.getCode()); verify(payload).writeInt1(FirebirdSQLInfoPacketType.DESCRIBE_END.getCode()); } + + @Test + void assertWriteUsesDefaultColumnLength() { + ShardingSphereColumn column = new ShardingSphereColumn("col", Types.INTEGER, false, false, false, true, false, true); + ShardingSphereTable table = new ShardingSphereTable("tbl", Collections.singleton(column), Collections.emptyList(), Collections.emptyList()); + FirebirdReturnColumnPacket packet = new FirebirdReturnColumnPacket(Collections.singletonList(FirebirdSQLInfoPacketType.LENGTH), + 1, table, column, "t", "c", "o", null); + packet.write(payload); + verify(payload).writeInt4LE(4); + } } From 66578772b1570fee6a9ed58cd6b737472770db59 Mon Sep 17 00:00:00 2001 From: vasilevskyy Date: Thu, 23 Oct 2025 14:53:43 +0300 Subject: [PATCH 11/11] add support for remaining dynamic Firebird column types --- .../data/loader/FirebirdColumnSizeLoader.java | 31 ++++++++++++++++--- .../loader/FirebirdColumnSizeLoaderTest.java | 15 +++++---- .../FirebirdReturnColumnPacketTest.java | 3 +- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java index a958022c91242..e0231e0a5ff1f 100644 --- a/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java +++ b/database/connector/dialect/firebird/src/main/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoader.java @@ -24,6 +24,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Types; import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -66,24 +67,27 @@ Map> load() throws SQLException { private Map loadTableColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName) throws SQLException { Map result = new HashMap<>(); - loadVarcharColumnSizes(connection, formattedTableName, result); + loadColumnSizesFromMetaData(connection, formattedTableName, result); loadBlobSegmentSizes(connection, formattedTableName, result); return result.isEmpty() ? Collections.emptyMap() : Collections.unmodifiableMap(result); } - private void loadVarcharColumnSizes(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { + private void loadColumnSizesFromMetaData(final MetaDataLoaderConnection connection, final String formattedTableName, final Map result) throws SQLException { try (ResultSet resultSet = connection.getMetaData().getColumns(connection.getCatalog(), connection.getSchema(), formattedTableName, "%")) { while (resultSet.next()) { if (!Objects.equals(formattedTableName, resultSet.getString("TABLE_NAME"))) { continue; } - String typeName = resultSet.getString("TYPE_NAME"); - if (null == typeName || !typeName.toUpperCase(Locale.ENGLISH).startsWith("VARCHAR")) { + int dataType = resultSet.getInt("DATA_TYPE"); + if (!isDynamicLengthType(dataType)) { continue; } String columnName = resultSet.getString("COLUMN_NAME"); if (null != columnName) { - result.put(columnName.toUpperCase(Locale.ENGLISH), resultSet.getInt("COLUMN_SIZE")); + int columnSize = resultSet.getInt("COLUMN_SIZE"); + if (!resultSet.wasNull() && columnSize > 0) { + result.put(columnName.toUpperCase(Locale.ENGLISH), columnSize); + } } } } @@ -107,4 +111,21 @@ private void loadBlobSegmentSizes(final MetaDataLoaderConnection connection, fin } } } + + private boolean isDynamicLengthType(final int dataType) { + switch (dataType) { + case Types.CHAR: + case Types.NCHAR: + case Types.VARCHAR: + case Types.NVARCHAR: + case Types.LONGVARCHAR: + case Types.LONGNVARCHAR: + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return true; + default: + return false; + } + } } diff --git a/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java index 1e10d74d389f5..c30223aedcfcc 100644 --- a/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java +++ b/database/connector/dialect/firebird/src/test/java/org/apache/shardingsphere/database/connector/firebird/metadata/data/loader/FirebirdColumnSizeLoaderTest.java @@ -32,6 +32,7 @@ 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.Map; @@ -42,6 +43,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.assertFalse; @ExtendWith(MockitoExtension.class) class FirebirdColumnSizeLoaderTest { @@ -83,11 +85,11 @@ void setUp() throws SQLException { @Test void assertLoadReturnsCombinedVarcharAndBlobSizes() throws SQLException { - when(columnsResultSet.next()).thenReturn(true, true, false); - when(columnsResultSet.getString("TABLE_NAME")).thenReturn("TEST_TABLE", "TEST_TABLE"); - when(columnsResultSet.getString("TYPE_NAME")).thenReturn("varchar", "integer"); - when(columnsResultSet.getString("COLUMN_NAME")).thenReturn("varchar_col", "ignored_col"); - when(columnsResultSet.getInt("COLUMN_SIZE")).thenReturn(128, 256); + when(columnsResultSet.next()).thenReturn(true, true, true, false); + when(columnsResultSet.getString("TABLE_NAME")).thenReturn("TEST_TABLE", "TEST_TABLE", "TEST_TABLE"); + when(columnsResultSet.getInt("DATA_TYPE")).thenReturn(Types.CHAR, Types.BIGINT, Types.BLOB); + when(columnsResultSet.getString("COLUMN_NAME")).thenReturn("char_col", "bigint_col", "ignored_blob"); + when(columnsResultSet.getInt("COLUMN_SIZE")).thenReturn(128, 19, 0); when(blobResultSet.next()).thenReturn(true, true, false); when(blobResultSet.getString("COLUMN_NAME")).thenReturn(" blob_col ", " "); when(blobResultSet.getInt("SEGMENT_SIZE")).thenReturn(2048, 4096); @@ -95,7 +97,8 @@ void assertLoadReturnsCombinedVarcharAndBlobSizes() throws SQLException { assertThat(actual, hasKey("test_table")); Map tableSizes = actual.get("test_table"); assertThat(tableSizes.size(), is(2)); - assertThat(tableSizes.get("VARCHAR_COL"), is(128)); + assertThat(tableSizes.get("CHAR_COL"), is(128)); + assertFalse(tableSizes.containsKey("BIGINT_COL")); assertThat(tableSizes.get("BLOB_COL"), is(2048)); verify(preparedStatement).setString(1, "TEST_TABLE"); } diff --git a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java index ee42eb9b57921..0f556d6fcdc24 100644 --- a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java +++ b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/prepare/FirebirdReturnColumnPacketTest.java @@ -41,7 +41,7 @@ class FirebirdReturnColumnPacketTest { @Test void assertWrite() { - ShardingSphereColumn column = new ShardingSphereColumn("col", Types.INTEGER, false, false, false, true, false, true); + ShardingSphereColumn column = new ShardingSphereColumn("col", Types.VARCHAR, false, false, false, true, false, true); ShardingSphereTable table = new ShardingSphereTable("tbl", Collections.singleton(column), Collections.emptyList(), Collections.emptyList()); FirebirdReturnColumnPacket packet = new FirebirdReturnColumnPacket(Arrays.asList( FirebirdSQLInfoPacketType.SQLDA_SEQ, @@ -59,6 +59,7 @@ void assertWrite() { packet.write(payload); verify(payload).writeInt1(FirebirdSQLInfoPacketType.SQLDA_SEQ.getCode()); verify(payload).writeInt1(FirebirdSQLInfoPacketType.DESCRIBE_END.getCode()); + verify(payload).writeInt4LE(99); } @Test