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 ab96f2464a4 Fix PostgreSQL statement descriptions with derived 
projections (#40201)
ab96f2464a4 is described below

commit ab96f2464a4643308b2b5be3ddaa2414be2d40df
Author: cangen185-sudo <[email protected]>
AuthorDate: Sat Oct 17 09:54:46 2026 +0800

    Fix PostgreSQL statement descriptions with derived projections (#40201)
    
    Fixes #40194
---
 RELEASE-NOTES.md                                   |  1 +
 .../describe/PostgreSQLComDescribeExecutor.java    | 10 +++-
 .../PostgreSQLComDescribeExecutorTest.java         | 57 ++++++++++++++++++++++
 3 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index e59ea5f87ab..d6632d2fa95 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -68,6 +68,7 @@
 1. JDBC: Fix statement close invalidating live result sets of other statements 
on the same connection - 
[#39503](https://github.com/apache/shardingsphere/pull/39503)
 1. Proxy: Resolve MySQL prepared statement parameter columns for where clause 
- [#38382](https://github.com/apache/shardingsphere/pull/38382)
 1. Proxy: Resolve PostgreSQL prepared statement parameter types before bind - 
[#38384](https://github.com/apache/shardingsphere/pull/38384)
+1. Proxy: Exclude derived projections from PostgreSQL prepared statement row 
descriptions - [#40194](https://github.com/apache/shardingsphere/issues/40194)
 1. Proxy: Resolve openGauss batch bind parameter types before reading values - 
[#38390](https://github.com/apache/shardingsphere/pull/38390)
 1. Proxy: Fix primary key metadata loss for aliased columns in mysql prepare 
probe path - [#38517](https://github.com/apache/shardingsphere/pull/38517)
 1. Proxy: Force PostgreSQL backend `client_encoding` to UTF8 and reject 
non-UTF8 values to keep pgjdbc parameter encoding consistent - 
[#38645](https://github.com/apache/shardingsphere/pull/38645)
diff --git 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutor.java
 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutor.java
index d8e0f8f4fa5..3c60d284168 100644
--- 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutor.java
+++ 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutor.java
@@ -28,6 +28,8 @@ import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.que
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.query.extended.describe.PostgreSQLComDescribePacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.type.ColumnTypeOIDLoader;
 import 
org.apache.shardingsphere.database.protocol.postgresql.type.ColumnTypeOIDResolver;
+import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
 import 
org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
@@ -255,8 +257,12 @@ public final class PostgreSQLComDescribeExecutor 
implements CommandExecutor {
             return;
         }
         Map<Integer, Integer> columnTypeOIDs = 
ColumnTypeOIDLoader.load(actualPreparedStatement.getConnection(), 
resultSetMetaData, columnTypeOIDResolver);
-        List<PostgreSQLColumnDescription> columnDescriptions = new 
ArrayList<>(resultSetMetaData.getColumnCount());
-        for (int columnIndex = 1; columnIndex <= 
resultSetMetaData.getColumnCount(); columnIndex++) {
+        SQLStatementContext sqlStatementContext = 
logicPreparedStatement.getSqlStatementContext();
+        int columnCount = sqlStatementContext instanceof 
SelectStatementContext && ((SelectStatementContext) 
sqlStatementContext).containsDerivedProjections()
+                ? ((SelectStatementContext) 
sqlStatementContext).getProjectionsContext().getExpandProjections().size()
+                : resultSetMetaData.getColumnCount();
+        List<PostgreSQLColumnDescription> columnDescriptions = new 
ArrayList<>(columnCount);
+        for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
             String columnName = resultSetMetaData.getColumnName(columnIndex);
             int columnType = resultSetMetaData.getColumnType(columnIndex);
             int columnLength = 
resultSetMetaData.getColumnDisplaySize(columnIndex);
diff --git 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutorTest.java
 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutorTest.java
index 2f9c5a3b4fc..035aa7f0ded 100644
--- 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutorTest.java
+++ 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/describe/PostgreSQLComDescribeExecutorTest.java
@@ -31,6 +31,8 @@ import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.que
 import 
org.apache.shardingsphere.database.protocol.postgresql.payload.PostgreSQLPacketPayload;
 import 
org.apache.shardingsphere.database.protocol.postgresql.type.ColumnTypeOIDLoader;
 import 
org.apache.shardingsphere.database.protocol.postgresql.type.ColumnTypeOIDResolver;
+import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
+import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.ProjectionsContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
@@ -58,6 +60,7 @@ import 
org.apache.shardingsphere.proxy.frontend.postgresql.command.query.extende
 import 
org.apache.shardingsphere.proxy.frontend.postgresql.command.query.extended.PostgreSQLServerPreparedStatement;
 import org.apache.shardingsphere.sql.parser.engine.api.CacheOption;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 import org.apache.shardingsphere.sqltranslator.rule.SQLTranslatorRule;
 import 
org.apache.shardingsphere.sqltranslator.rule.builder.DefaultSQLTranslatorRuleConfigurationBuilder;
@@ -473,6 +476,60 @@ class PostgreSQLComDescribeExecutorTest {
         }
     }
     
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("derivedProjections")
+    void assertDescribeWithDerivedProjections(final String name, final String 
sql, final int visibleColumnCount, final int physicalColumnCount, final String 
firstColumnName) throws SQLException {
+        when(packet.getType()).thenReturn('S');
+        when(packet.getName()).thenReturn("foo_describe");
+        SelectStatementContext sqlStatementContext = 
mock(SelectStatementContext.class);
+        
when(sqlStatementContext.getSqlStatement()).thenReturn((SelectStatement) 
SQL_PARSER_ENGINE.parse(sql, false));
+        
when(sqlStatementContext.containsDerivedProjections()).thenReturn(true);
+        ProjectionsContext projectionsContext = mock(ProjectionsContext.class);
+        
when(projectionsContext.getExpandProjections()).thenReturn(Collections.nCopies(visibleColumnCount,
 mock(Projection.class)));
+        
when(sqlStatementContext.getProjectionsContext()).thenReturn(projectionsContext);
+        Connection connection = mock(Connection.class);
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        ResultSetMetaData resultSetMetaData = mock(ResultSetMetaData.class);
+        
when(resultSetMetaData.getColumnCount()).thenReturn(physicalColumnCount);
+        when(resultSetMetaData.getColumnName(anyInt())).thenAnswer(invocation 
-> 1 == (int) invocation.getArgument(0) ? firstColumnName : "foo_column_" + 
invocation.getArgument(0));
+        
when(resultSetMetaData.getColumnType(anyInt())).thenReturn(Types.INTEGER);
+        when(resultSetMetaData.getColumnDisplaySize(anyInt())).thenReturn(11);
+        when(resultSetMetaData.getColumnTypeName(anyInt())).thenReturn("int4");
+        when(preparedStatement.getMetaData()).thenReturn(resultSetMetaData);
+        when(preparedStatement.getConnection()).thenReturn(connection);
+        when(connection.prepareStatement(sql)).thenReturn(preparedStatement);
+        ProxyDatabaseConnectionManager databaseConnectionManager = 
mock(ProxyDatabaseConnectionManager.class);
+        when(databaseConnectionManager.getConnections(any(), 
nullable(String.class), anyInt(), anyInt(), 
any(ConnectionMode.class))).thenReturn(Collections.singletonList(connection));
+        
when(connectionSession.getDatabaseConnectionManager()).thenReturn(databaseConnectionManager);
+        ContextManager contextManager = mockContextManager();
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        ConnectionContext connectionContext = mock(ConnectionContext.class);
+        
when(connectionContext.getCurrentDatabaseName()).thenReturn(Optional.of(DATABASE_NAME));
+        
when(connectionSession.getConnectionContext()).thenReturn(connectionContext);
+        
connectionSession.getServerPreparedStatementRegistry().addPreparedStatement("foo_describe",
+                new PostgreSQLServerPreparedStatement(sql, 
sqlStatementContext, new HintValueContext(), Collections.emptyList(), 
Collections.emptyList()));
+        columnTypeOIDs = Collections.singletonMap(1, 2249);
+        Iterator<DatabasePacket> actual = executor.execute().iterator();
+        actual.next();
+        List<PostgreSQLColumnDescription> actualColumns = 
getColumnDescriptions((PostgreSQLRowDescriptionPacket) actual.next());
+        assertThat(actualColumns.size(), is(visibleColumnCount));
+        assertThat(actualColumns.get(0).getColumnName(), is(firstColumnName));
+        assertThat(actualColumns.get(0).getTypeOID(), is(2249));
+        for (int i = 1; i < actualColumns.size(); i++) {
+            assertThat(actualColumns.get(i).getColumnIndex(), is(i + 1));
+            assertThat(actualColumns.get(i).getColumnName(), is("foo_column_" 
+ (i + 1)));
+            assertThat(actualColumns.get(i).getTypeOID(), 
is(PostgreSQLBinaryColumnType.INT4.getValue()));
+        }
+    }
+    
+    private static Stream<Arguments> derivedProjections() {
+        return Stream.of(
+                Arguments.of("order by projection", "SELECT id FROM t_order 
ORDER BY k", 1, 2, "foo_id"),
+                Arguments.of("aggregation projections", "SELECT AVG(id) FROM 
t_order", 1, 3, "foo_average"),
+                Arguments.of("expanded shorthand", "SELECT * FROM t_order 
ORDER BY id + 1", 4, 5, "foo_id"),
+                Arguments.of("user alias resembling a derived column", "SELECT 
id AS order_by_derived_0 FROM t_order ORDER BY k", 1, 2, "order_by_derived_0"));
+    }
+    
     @Test
     void assertDescribeSelectPreparedStatementWithNullMetaData() throws 
SQLException {
         when(packet.getType()).thenReturn('S');

Reply via email to