Repository: apache/shardingsphere (2026-07-15T12:59:43Z)

Issue:
PostgreSQL array parameter decoder throws StringIndexOutOfBoundsException for empty array literal {}
## Bug Report

### Which version of ShardingSphere did you use?
master @ 7568693e8ee

### Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
Proxy

### Expected behavior
Binding an empty PostgreSQL array literal `{}` as a parameter decodes to a zero-length array for every array type.

### Actual behavior
Decoding `{}` throws `StringIndexOutOfBoundsException` for all seven array types (int2/int4/int8/float4/float8/bool/string).

### Reason analyze (If you can)
`PostgreSQLArrayParameterDecoder.decodeText()` (`database/protocol/dialect/postgresql/.../bind/protocol/PostgreSQLArrayParameterDecoder.java`) computes the inner content `value.substring(1, value.length() - 1)`, which is `""` for `{}`. On JDK 8 `"".split(",")` returns `[""]` (length 1), not an empty array, so the stream calls `decodeElementText("")`, whose first line runs `"".charAt(0)` and throws. All seven public decode methods delegate to this shared method, so all crash.

### Steps to reproduce the behavior, such as: SQL to execute, sharding rule configuration, when exception occur etc.
Through the Proxy PostgreSQL front-end, bind an empty array parameter. `PostgreSQLStringArrayBinaryProtocolValue.read()` calls the decoder with `isBinary = '{' != bytes[0]`; for `{}`, `bytes[0] == '{'`, so the text path runs and crashes.

### Example codes for reproduce this issue (such as a github link).
N/A



Discussion:

Patch:
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 6cf2ae41dfb9d..ee7f2f7b5dfee 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -43,6 +43,8 @@
 1. Sharding: Compute the Snowflake key generator epoch in UTC instead of the JVM default timezone - [#38932](https://github.com/apache/shardingsphere/pull/38932)
 1. Proxy: Fix MySQL BLOB data corruption when string-like prepared statement parameters target BLOB columns - [#39072](https://github.com/apache/shardingsphere/pull/39072)
 1. Agent: Fix wrong target class name in StaticMethodAdviceExecutor error logs - [#39077](https://github.com/apache/shardingsphere/pull/39077)
+1. Metadata: Fix Oracle metadata version comparison skipping identity and collation columns on 18c and later - [#39104](https://github.com/apache/shardingsphere/pull/39104)
+1. Proxy: Fix empty array literal decoding in PostgreSQL array parameter decoder - [#39137](https://github.com/apache/shardingsphere/pull/39137)
 
 ### Enhancements
 
diff --git a/database/protocol/dialect/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoder.java b/database/protocol/dialect/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoder.java
index d945f5d97ca26..cb31c22e4aee4 100644
--- a/database/protocol/dialect/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoder.java
+++ b/database/protocol/dialect/postgresql/src/main/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoder.java
@@ -24,6 +24,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.stream.Collectors;
 
 /**
@@ -170,7 +171,11 @@ public String[] decodeStringArray(final byte[] parameterBytes, final boolean isB
     private Collection<String> decodeText(final String value) {
         Preconditions.checkArgument(value.length() >= 2, "value length less than 2");
         Preconditions.checkArgument('{' == value.charAt(0) && '}' == value.charAt(value.length() - 1), "value not start with '{' or not end with '}'");
-        String[] elements = value.substring(1, value.length() - 1).split(",");
+        String content = value.substring(1, value.length() - 1);
+        if (content.isEmpty()) {
+            return Collections.emptyList();
+        }
+        String[] elements = content.split(",");
         return Arrays.stream(elements).map(each -> "NULL".equals(each) ? null : decodeElementText(each)).collect(Collectors.toList());
     }
     


Test patch:
diff --git a/database/protocol/dialect/postgresql/src/test/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoderTest.java b/database/protocol/dialect/postgresql/src/test/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoderTest.java
index c1b8cff3fe8d1..c16fb393286aa 100644
--- a/database/protocol/dialect/postgresql/src/test/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoderTest.java
+++ b/database/protocol/dialect/postgresql/src/test/java/org/apache/shardingsphere/database/protocol/postgresql/packet/command/query/extended/bind/protocol/PostgreSQLArrayParameterDecoderTest.java
@@ -106,4 +106,22 @@ void assertParseStringArrayWithNullTextMode() {
         assertThat(actual[1], is("b"));
         assertNull(actual[2]);
     }
+    
+    @Test
+    void assertParseStringArrayEmptyTextMode() {
+        String[] actual = DECODER.decodeStringArray("{}".getBytes(), false);
+        assertThat(actual.length, is(0));
+    }
+    
+    @Test
+    void assertParseInt4ArrayEmptyTextMode() {
+        int[] actual = DECODER.decodeInt4Array("{}".getBytes(), false);
+        assertThat(actual.length, is(0));
+    }
+    
+    @Test
+    void assertParseBoolArrayEmptyTextMode() {
+        boolean[] actual = DECODER.decodeBoolArray("{}".getBytes(), false);
+        assertThat(actual.length, is(0));
+    }
 }