This is an automated email from the ASF dual-hosted git repository.

terrymanu 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 f43db5795ee Fix firebird select string literal (#40173)
f43db5795ee is described below

commit f43db5795ee92e2d64f544ad6a5f6e19c27309bd
Author: Maxim Igoshin <[email protected]>
AuthorDate: Sun Oct 18 07:03:07 2026 +0300

    Fix firebird select string literal (#40173)
    
    * Parse and describe string literals in Firebird select lists
    
    * Retrigger CI
    
    * Retrigger CI
    
    * Fix CODE_OF_CONDUCT violations in Firebird select string literal describe
    
    * Fix CODE_OF_CONDUCT violations in Firebird select string literal describe
---
 .../src/main/antlr4/imports/firebird/BaseRule.g4   |  3 +-
 .../FirebirdPrepareStatementCommandExecutor.java   | 20 ++++++++++++-
 ...irebirdPrepareStatementCommandExecutorTest.java | 33 ++++++++++++++++++++++
 .../parser/src/main/resources/case/dml/select.xml  | 14 +++++++++
 .../main/resources/sql/supported/dml/select.xml    |  1 +
 5 files changed, 68 insertions(+), 3 deletions(-)

diff --git 
a/parser/sql/engine/dialect/firebird/src/main/antlr4/imports/firebird/BaseRule.g4
 
b/parser/sql/engine/dialect/firebird/src/main/antlr4/imports/firebird/BaseRule.g4
index 8901ee0789f..ae1d3b57f45 100644
--- 
a/parser/sql/engine/dialect/firebird/src/main/antlr4/imports/firebird/BaseRule.g4
+++ 
b/parser/sql/engine/dialect/firebird/src/main/antlr4/imports/firebird/BaseRule.g4
@@ -244,8 +244,7 @@ comparisonOperator
     ;
 
 predicate
-    :
-    | bitExpr NOT? IN subquery
+    : bitExpr NOT? IN subquery
     | bitExpr NOT? IN LP_ expr (COMMA_ expr)* RP_
     | bitExpr NOT? BETWEEN bitExpr AND predicate
     | bitExpr NOT? LIKE simpleExpr (ESCAPE simpleExpr)?
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 697066b8e60..443c9494b02 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 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.Co
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.RollbackStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.SavepointStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import org.jspecify.annotations.Nullable;
 
 import java.sql.SQLException;
 import java.sql.Types;
@@ -106,6 +107,8 @@ import java.util.OptionalInt;
 @RequiredArgsConstructor
 public final class FirebirdPrepareStatementCommandExecutor implements 
CommandExecutor {
     
+    private static final ShardingSphereTable LITERAL_TABLE = new 
ShardingSphereTable(null, Collections.emptyList(), Collections.emptyList(), 
Collections.emptyList());
+    
     private final FirebirdPrepareStatementPacket packet;
     
     private final ConnectionSession connectionSession;
@@ -430,7 +433,7 @@ public final class FirebirdPrepareStatementCommandExecutor 
implements CommandExe
             } else if (value instanceof Number) {
                 type = Types.NUMERIC;
             }
-            processCustomColumn(null, null, expr.getAlias().orElse(null), 
type, describeColumns, requestedItems, columnCount);
+            processLiteralColumn(value, expr.getAlias().orElse(null), type, 
describeColumns, requestedItems, columnCount);
         }
     }
     
@@ -470,6 +473,21 @@ public final class FirebirdPrepareStatementCommandExecutor 
implements CommandExe
         }
     }
     
+    @HighFrequencyInvocation
+    private void processLiteralColumn(@Nullable final Object value, @Nullable 
final IdentifierValue columnAlias, final int dataType, final 
Collection<FirebirdReturnColumnPacket> describeColumns,
+                                      final 
Collection<FirebirdSQLInfoPacketType> requestedItems, final int columnCount) {
+        ShardingSphereColumn column = new ShardingSphereColumn("CONSTANT", 
dataType, false, false, true, true, false, false);
+        String columnAliasString = null == columnAlias ? column.getName() : 
columnAlias.getValue();
+        String owner = 
connectionSession.getConnectionContext().getGrantee().getUsername();
+        Integer columnLength = value instanceof String ? 
getStringLiteralLength((String) value) : null;
+        describeColumns.add(new FirebirdReturnColumnPacket(requestedItems, 
columnCount, LITERAL_TABLE, column, null, columnAliasString, owner, 
columnLength, false, null));
+    }
+    
+    private int getStringLiteralLength(final String value) {
+        String text = value.replace("''", "'");
+        return text.codePointCount(0, text.length());
+    }
+    
     private void processCustomColumn(final String tableName, final String 
columnName, final IdentifierValue columnAlias, final int dataType,
                                      final 
Collection<FirebirdReturnColumnPacket> describeColumns, final 
Collection<FirebirdSQLInfoPacketType> requestedItems, final int columnCount) {
         ShardingSphereTable table = new ShardingSphereTable(tableName, 
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
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 45829f65c58..8f3249ff0cb 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
@@ -70,6 +70,7 @@ import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
 
+import java.nio.charset.StandardCharsets;
 import java.sql.Types;
 import java.util.Arrays;
 import java.util.Collection;
@@ -85,6 +86,7 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -289,6 +291,37 @@ class FirebirdPrepareStatementCommandExecutorTest {
         return returnPacket.getDescribeSelect().get(0);
     }
     
+    @Test
+    void assertDescribeLiterals() throws Exception {
+        when(packet.getSQL()).thenReturn("SELECT 'it''s', 1 AS one FROM 
foo_tbl");
+        when(packet.nextItem()).thenReturn(true, true, true, true, true, true, 
true, false);
+        when(packet.getCurrentItem()).thenReturn(
+                FirebirdSQLInfoPacketType.STMT_TYPE,
+                FirebirdSQLInfoPacketType.SELECT,
+                FirebirdSQLInfoPacketType.FIELD,
+                FirebirdSQLInfoPacketType.FIELD,
+                FirebirdSQLInfoPacketType.ALIAS,
+                FirebirdSQLInfoPacketType.ALIAS,
+                FirebirdSQLInfoPacketType.LENGTH,
+                FirebirdSQLInfoPacketType.LENGTH,
+                FirebirdSQLInfoPacketType.DESCRIBE_END,
+                FirebirdSQLInfoPacketType.DESCRIBE_END);
+        FirebirdPrepareStatementCommandExecutor executor = new 
FirebirdPrepareStatementCommandExecutor(packet, connectionSession);
+        Collection<DatabasePacket> actual = executor.execute();
+        FirebirdPrepareStatementReturnPacket actualReturnPacket = 
(FirebirdPrepareStatementReturnPacket) ((FirebirdGenericResponsePacket) 
actual.iterator().next()).getData();
+        assertThat(actualReturnPacket.getDescribeSelect().size(), is(2));
+        FirebirdPacketPayload stringPayload = 
mock(FirebirdPacketPayload.class);
+        when(stringPayload.getCharset()).thenReturn(StandardCharsets.UTF_8);
+        actualReturnPacket.getDescribeSelect().get(0).write(stringPayload);
+        verify(stringPayload, 
times(2)).writeBytes("CONSTANT".getBytes(StandardCharsets.UTF_8));
+        verify(stringPayload).writeInt4LE(4);
+        FirebirdPacketPayload numberPayload = 
mock(FirebirdPacketPayload.class);
+        when(numberPayload.getCharset()).thenReturn(StandardCharsets.UTF_8);
+        actualReturnPacket.getDescribeSelect().get(1).write(numberPayload);
+        
verify(numberPayload).writeBytes("CONSTANT".getBytes(StandardCharsets.UTF_8));
+        
verify(numberPayload).writeBytes("one".getBytes(StandardCharsets.UTF_8));
+    }
+    
     @Test
     void 
assertExecuteWithValidStatementHandleCleansPreviousPreparedStatementResources() 
throws Exception {
         
connectionSession.getServerPreparedStatementRegistry().addPreparedStatement(1, 
new FirebirdServerPreparedStatement("SELECT 0", 
mock(SelectStatementContext.class), new HintValueContext()));
diff --git a/test/it/parser/src/main/resources/case/dml/select.xml 
b/test/it/parser/src/main/resources/case/dml/select.xml
index 2f76b6ba954..c27acb878f2 100644
--- a/test/it/parser/src/main/resources/case/dml/select.xml
+++ b/test/it/parser/src/main/resources/case/dml/select.xml
@@ -13403,6 +13403,20 @@
         </from>
     </select>
 
+    <select sql-case-id="select_string_literal_projection_firebird">
+        <projections start-index="7" stop-index="19">
+            <expression-projection text="x" start-index="7" stop-index="9">
+                <expr>
+                    <literal-expression value="x" start-index="7" 
stop-index="9" />
+                </expr>
+            </expression-projection>
+            <column-projection name="order_id" start-index="12" 
stop-index="19" />
+        </projections>
+        <from>
+            <simple-table name="t_order" start-index="26" stop-index="32" />
+        </from>
+    </select>
+
     <select sql-case-id="select_gen_id_with_identifier_firebird">
         <projections start-index="7" stop-index="20">
             <expression-projection text="GEN_ID(seq, 1)" start-index="7" 
stop-index="20">
diff --git a/test/it/parser/src/main/resources/sql/supported/dml/select.xml 
b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
index a2d82b48af1..fc3bafd6f2a 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select.xml
@@ -449,6 +449,7 @@
     <sql-case id="select_having_firebird" value="SELECT user_id, COUNT(*) AS 
cnt FROM t_order GROUP BY user_id HAVING COUNT(*) > 1" db-types="Firebird"/>
     <sql-case id="select_function_table_firebird" value="SELECT * FROM foo(1)" 
db-types="Firebird"/>
     <sql-case id="select_cast_int_firebird" value="SELECT CAST(user_id AS INT) 
FROM t_order" db-types="Firebird"/>
+    <sql-case id="select_string_literal_projection_firebird" value="SELECT 
'x', order_id FROM t_order" db-types="Firebird"/>
     <sql-case id="select_gen_id_with_identifier_firebird" value="SELECT 
GEN_ID(seq, 1) FROM t_order" db-types="Firebird"/>
     <sql-case id="select_gen_id_with_parameters_firebird" value="SELECT 
GEN_ID(?, ?) FROM t_order" db-types="Firebird" case-types="Placeholder"/>
     <sql-case id="select_variable_default_firebird" value="SELECT DEFAULT FROM 
t_order" db-types="Firebird"/>

Reply via email to