This is an automated email from the ASF dual-hosted git repository.
zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 5f970d1404e Fix COUNT(*) handling for Firebird (#37494)
5f970d1404e is described below
commit 5f970d1404e23c5ee23ba44053f3c8049df5d778
Author: Maxim Sentyabrskiy <[email protected]>
AuthorDate: Thu Dec 25 07:47:25 2025 +0300
Fix COUNT(*) handling for Firebird (#37494)
* fix COUNT(*) realisation
* fix COUNT(*) without alias
* fixes
---
distribution/bom/pom.xml | 5 ++
.../binder/dialect}/firebird/pom.xml | 7 +--
.../FirebirdProjectionIdentifierExtractor.java | 54 ++++++++++++++++
....extractor.DialectProjectionIdentifierExtractor | 18 ++++++
.../FirebirdProjectionIdentifierExtractorTest.java | 72 ++++++++++++++++++++++
infra/binder/dialect/pom.xml | 1 +
jdbc-dialect/firebird/pom.xml | 6 ++
proxy/dialect/firebird/pom.xml | 6 ++
.../FirebirdPrepareStatementCommandExecutor.java | 4 +-
...irebirdPrepareStatementCommandExecutorTest.java | 39 +++++++++++-
10 files changed, 206 insertions(+), 6 deletions(-)
diff --git a/distribution/bom/pom.xml b/distribution/bom/pom.xml
index 878ce911324..a5c29fe7723 100644
--- a/distribution/bom/pom.xml
+++ b/distribution/bom/pom.xml
@@ -195,6 +195,11 @@
<artifactId>shardingsphere-infra-binder-opengauss</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.shardingsphere</groupId>
+ <artifactId>shardingsphere-infra-binder-firebird</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-infra-checker</artifactId>
diff --git a/jdbc-dialect/firebird/pom.xml
b/infra/binder/dialect/firebird/pom.xml
similarity index 86%
copy from jdbc-dialect/firebird/pom.xml
copy to infra/binder/dialect/firebird/pom.xml
index 0c577f9ec43..81ed0a9dc1b 100644
--- a/jdbc-dialect/firebird/pom.xml
+++ b/infra/binder/dialect/firebird/pom.xml
@@ -20,18 +20,17 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.shardingsphere</groupId>
- <artifactId>shardingsphere-jdbc-dialect</artifactId>
+ <artifactId>shardingsphere-infra-binder-dialect</artifactId>
<version>5.5.3-SNAPSHOT</version>
</parent>
- <artifactId>shardingsphere-jdbc-dialect-firebird</artifactId>
+ <artifactId>shardingsphere-infra-binder-firebird</artifactId>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
- <artifactId>shardingsphere-parser-sql-engine-firebird</artifactId>
+ <artifactId>shardingsphere-infra-binder-core</artifactId>
<version>${project.version}</version>
- <scope>runtime</scope>
</dependency>
</dependencies>
</project>
diff --git
a/infra/binder/dialect/firebird/src/main/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractor.java
b/infra/binder/dialect/firebird/src/main/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractor.java
new file mode 100644
index 00000000000..800c91e17f2
--- /dev/null
+++
b/infra/binder/dialect/firebird/src/main/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractor.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.infra.binder.firebird;
+
+import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+
+/**
+ * Projection identifier extractor for Firebird.
+ */
+public final class FirebirdProjectionIdentifierExtractor implements
DialectProjectionIdentifierExtractor {
+
+ @Override
+ public String getIdentifierValue(final IdentifierValue identifierValue) {
+ return identifierValue.getValue();
+ }
+
+ @Override
+ public String getColumnNameFromFunction(final String functionName, final
String functionExpression) {
+ return functionName;
+ }
+
+ @Override
+ public String getColumnNameFromExpression(final ExpressionSegment
expressionSegment) {
+ return expressionSegment.getText();
+ }
+
+ @Override
+ public String getColumnNameFromSubquery(final SubqueryProjectionSegment
subquerySegment) {
+ return subquerySegment.getText();
+ }
+
+ @Override
+ public String getDatabaseType() {
+ return "Firebird";
+ }
+}
diff --git
a/infra/binder/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor
b/infra/binder/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor
new file mode 100644
index 00000000000..4dd472289ba
--- /dev/null
+++
b/infra/binder/dialect/firebird/src/main/resources/META-INF/services/org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor
@@ -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.binder.firebird.FirebirdProjectionIdentifierExtractor
diff --git
a/infra/binder/dialect/firebird/src/test/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractorTest.java
b/infra/binder/dialect/firebird/src/test/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractorTest.java
new file mode 100644
index 00000000000..a5e57829842
--- /dev/null
+++
b/infra/binder/dialect/firebird/src/test/java/org/apache/shardingsphere/infra/binder/firebird/FirebirdProjectionIdentifierExtractorTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.binder.firebird;
+
+import
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor;
+import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.AggregationProjection;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import
org.apache.shardingsphere.sql.parser.statement.core.enums.AggregationType;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationProjectionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class FirebirdProjectionIdentifierExtractorTest {
+
+ private final DatabaseType databaseType =
TypedSPILoader.getService(DatabaseType.class, "Firebird");
+
+ private final DialectProjectionIdentifierExtractor extractor =
DatabaseTypedSPILoader.getService(DialectProjectionIdentifierExtractor.class,
databaseType);
+
+ @Test
+ void assertGetIdentifierValue() {
+ assertThat(extractor.getIdentifierValue(new IdentifierValue("value")),
is("value"));
+ }
+
+ @Test
+ void assertGetColumnNameFromFunction() {
+ assertThat(extractor.getColumnNameFromFunction("COUNT", "COUNT(*)"),
is("COUNT"));
+ }
+
+ @Test
+ void assertGetColumnNameFromExpression() {
+ ExpressionSegment expressionSegment = mock(ExpressionSegment.class);
+ when(expressionSegment.getText()).thenReturn("expr");
+ assertThat(extractor.getColumnNameFromExpression(expressionSegment),
is("expr"));
+ }
+
+ @Test
+ void assertGetColumnNameFromSubquery() {
+ SubqueryProjectionSegment subquerySegment =
mock(SubqueryProjectionSegment.class);
+ when(subquerySegment.getText()).thenReturn("sub");
+ assertThat(extractor.getColumnNameFromSubquery(subquerySegment),
is("sub"));
+ }
+
+ @Test
+ void assertAggregationProjectionColumnLabelWithoutAlias() {
+ AggregationProjection projection = new
AggregationProjection(AggregationType.COUNT, new
AggregationProjectionSegment(0, 0, AggregationType.COUNT, "COUNT(*)"), null,
databaseType);
+ assertThat(projection.getColumnLabel(), is("COUNT"));
+ }
+}
diff --git a/infra/binder/dialect/pom.xml b/infra/binder/dialect/pom.xml
index ac4fdba1ce5..e0c12d58475 100644
--- a/infra/binder/dialect/pom.xml
+++ b/infra/binder/dialect/pom.xml
@@ -33,5 +33,6 @@
<module>oracle</module>
<module>sqlserver</module>
<module>opengauss</module>
+ <module>firebird</module>
</modules>
</project>
diff --git a/jdbc-dialect/firebird/pom.xml b/jdbc-dialect/firebird/pom.xml
index 0c577f9ec43..d6fe9cead16 100644
--- a/jdbc-dialect/firebird/pom.xml
+++ b/jdbc-dialect/firebird/pom.xml
@@ -33,5 +33,11 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.shardingsphere</groupId>
+ <artifactId>shardingsphere-infra-binder-firebird</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/proxy/dialect/firebird/pom.xml b/proxy/dialect/firebird/pom.xml
index 38bff61ce2a..6a22757f44d 100644
--- a/proxy/dialect/firebird/pom.xml
+++ b/proxy/dialect/firebird/pom.xml
@@ -40,6 +40,12 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.shardingsphere</groupId>
+ <artifactId>shardingsphere-infra-binder-firebird</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
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 e80c774d146..7a425dd11d8 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
@@ -89,6 +89,7 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.OptionalInt;
+import java.util.Locale;
/**
* Firebird prepare transaction command executor.
@@ -413,13 +414,14 @@ public final class
FirebirdPrepareStatementCommandExecutor implements CommandExe
private int getFunctionType(final String functionName) {
// TODO add proper coalesce and other conditional functions return
types
- switch (functionName) {
+ switch (functionName.toLowerCase(Locale.ENGLISH)) {
case "substring":
case "current_role":
case "current_user":
case "coalesce":
return Types.VARCHAR;
case "gen_id":
+ case "count":
return Types.BIGINT;
case "current_timestamp":
return Types.TIMESTAMP;
diff --git
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutorTest.java
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutorTest.java
index 77ca09d0714..aa517dbe1c1 100644
---
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutorTest.java
+++
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/prepare/FirebirdPrepareStatementCommandExecutorTest.java
@@ -18,11 +18,14 @@
package
org.apache.shardingsphere.proxy.frontend.firebird.command.query.statement.prepare;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.FirebirdBinaryColumnType;
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;
import
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.prepare.FirebirdPrepareStatementPacket;
import
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.prepare.FirebirdPrepareStatementReturnPacket;
+import
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.statement.prepare.FirebirdReturnColumnPacket;
import
org.apache.shardingsphere.database.protocol.firebird.packet.generic.FirebirdGenericResponsePacket;
+import
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPacketPayload;
import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
import
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
@@ -31,9 +34,13 @@ import
org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
+import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
+import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
import
org.apache.shardingsphere.infra.metadata.statistics.ShardingSphereStatistics;
+import org.apache.shardingsphere.infra.metadata.user.Grantee;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
import org.apache.shardingsphere.parser.config.SQLParserRuleConfiguration;
import org.apache.shardingsphere.parser.rule.SQLParserRule;
@@ -49,9 +56,11 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+import java.sql.Types;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;
@@ -59,6 +68,8 @@ import java.util.Properties;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(AutoMockExtension.class)
@@ -76,8 +87,10 @@ class FirebirdPrepareStatementCommandExecutorTest {
@BeforeEach
void setUp() {
+ ConnectionContext connectionContext = new
ConnectionContext(Collections::emptySet, new Grantee("foo_user"));
when(connectionSession.getServerPreparedStatementRegistry()).thenReturn(new
ServerPreparedStatementRegistry());
when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
+
when(connectionSession.getConnectionContext()).thenReturn(connectionContext);
when(packet.getSQL()).thenReturn("SELECT 1");
when(packet.getHintValueContext()).thenReturn(new HintValueContext());
when(packet.isValidStatementHandle()).thenReturn(true);
@@ -90,7 +103,9 @@ class FirebirdPrepareStatementCommandExecutorTest {
private MetaDataContexts createMetaDataContexts() {
SQLParserRule parserRule = new SQLParserRule(new
SQLParserRuleConfiguration(new CacheOption(128, 1024L), new CacheOption(128,
1024L)));
RuleMetaData globalRuleMetaData = new
RuleMetaData(Collections.singleton(parserRule));
- ShardingSphereSchema schema = new ShardingSphereSchema("foo_db",
Collections.emptyList(), Collections.emptyList());
+ ShardingSphereColumn column = new ShardingSphereColumn("id",
Types.INTEGER, false, false, true, true, false, true);
+ ShardingSphereTable table = new ShardingSphereTable("foo_tbl",
Collections.singleton(column), Collections.emptyList(),
Collections.emptyList());
+ ShardingSphereSchema schema = new ShardingSphereSchema("foo_db",
Collections.singleton(table), Collections.emptyList());
ShardingSphereDatabase database = new ShardingSphereDatabase(
"foo_db", databaseType, new
ResourceMetaData(Collections.emptyMap()), new
RuleMetaData(Collections.emptyList()), Collections.singleton(schema));
ShardingSphereMetaData metaData = new ShardingSphereMetaData(
@@ -109,4 +124,26 @@ class FirebirdPrepareStatementCommandExecutorTest {
assertThat(preparedStatement.getSql(), is("SELECT 1"));
assertThat(preparedStatement.getSqlStatementContext(),
isA(SelectStatementContext.class));
}
+
+ @Test
+ void assertDescribeCountReturnsBigintType() {
+ when(packet.getSQL()).thenReturn("SELECT COUNT(*) FROM foo_tbl");
+ when(packet.nextItem()).thenReturn(true, true, true, true, true,
false);
+ when(packet.getCurrentItem()).thenReturn(
+ FirebirdSQLInfoPacketType.STMT_TYPE,
+ FirebirdSQLInfoPacketType.SELECT,
+ FirebirdSQLInfoPacketType.TYPE,
+ FirebirdSQLInfoPacketType.TYPE,
+ FirebirdSQLInfoPacketType.DESCRIBE_END,
+ FirebirdSQLInfoPacketType.DESCRIBE_END);
+ FirebirdPrepareStatementCommandExecutor executor = new
FirebirdPrepareStatementCommandExecutor(packet, connectionSession);
+ Collection<DatabasePacket> actual = executor.execute();
+ FirebirdGenericResponsePacket responsePacket =
(FirebirdGenericResponsePacket) actual.iterator().next();
+ FirebirdPrepareStatementReturnPacket returnPacket =
(FirebirdPrepareStatementReturnPacket) responsePacket.getData();
+ assertThat(returnPacket.getDescribeSelect().size(), is(1));
+ FirebirdPacketPayload payload = mock(FirebirdPacketPayload.class,
Mockito.RETURNS_DEEP_STUBS);
+ FirebirdReturnColumnPacket columnPacket =
returnPacket.getDescribeSelect().get(0);
+ columnPacket.write(payload);
+ verify(payload).writeInt4LE(FirebirdBinaryColumnType.INT64.getValue()
+ 1);
+ }
}