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');