diff --git a/distribution/bom/pom.xml b/distribution/bom/pom.xml
index 878ce91132430..a5c29fe7723d9 100644
--- a/distribution/bom/pom.xml
+++ b/distribution/bom/pom.xml
@@ -195,6 +195,11 @@
shardingsphere-infra-binder-opengauss
${project.version}
+
+ org.apache.shardingsphere
+ shardingsphere-infra-binder-firebird
+ ${project.version}
+
org.apache.shardingsphere
shardingsphere-infra-checker
diff --git a/infra/binder/dialect/firebird/pom.xml b/infra/binder/dialect/firebird/pom.xml
new file mode 100644
index 0000000000000..81ed0a9dc1b43
--- /dev/null
+++ b/infra/binder/dialect/firebird/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ 4.0.0
+
+ org.apache.shardingsphere
+ shardingsphere-infra-binder-dialect
+ 5.5.3-SNAPSHOT
+
+ shardingsphere-infra-binder-firebird
+ ${project.artifactId}
+
+
+
+ org.apache.shardingsphere
+ shardingsphere-infra-binder-core
+ ${project.version}
+
+
+
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 0000000000000..800c91e17f243
--- /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 0000000000000..4dd472289ba42
--- /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 0000000000000..a5e5782984237
--- /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 ac4fdba1ce5e1..e0c12d58475c9 100644
--- a/infra/binder/dialect/pom.xml
+++ b/infra/binder/dialect/pom.xml
@@ -33,5 +33,6 @@
oracle
sqlserver
opengauss
+ firebird
diff --git a/jdbc-dialect/firebird/pom.xml b/jdbc-dialect/firebird/pom.xml
index 0c577f9ec43c1..d6fe9cead16ee 100644
--- a/jdbc-dialect/firebird/pom.xml
+++ b/jdbc-dialect/firebird/pom.xml
@@ -33,5 +33,11 @@
${project.version}
runtime
+
+ org.apache.shardingsphere
+ shardingsphere-infra-binder-firebird
+ ${project.version}
+ runtime
+
diff --git a/proxy/dialect/firebird/pom.xml b/proxy/dialect/firebird/pom.xml
index 38bff61ce2ab7..6a22757f44d93 100644
--- a/proxy/dialect/firebird/pom.xml
+++ b/proxy/dialect/firebird/pom.xml
@@ -40,6 +40,12 @@
${project.version}
runtime
+
+ org.apache.shardingsphere
+ shardingsphere-infra-binder-firebird
+ ${project.version}
+ runtime
+
org.apache.shardingsphere
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 e80c774d1468e..7a425dd11d8b2 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.LinkedList;
import java.util.Map;
import java.util.OptionalInt;
+import java.util.Locale;
/**
* Firebird prepare transaction command executor.
@@ -413,13 +414,14 @@ private void processExpressionProjection(final ExpressionProjection expr, final
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 77ca09d0714dc..aa517dbe1c19f 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.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.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 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 @@ void setUp() {
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 @@ void assertExecute() {
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 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);
+ }
}