Skip to content

Commit 45bb9aa

Browse files
authored
#2559 Fix psycopg array parameter handling for Cypher queries (#2606)
1 parent 83d91d9 commit 45bb9aa

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

e2e-python/tests/test_arcadedb.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,36 @@ def test_psycopg2_with_positional_parameterized_query():
243243
assert 'Stout' in beer
244244
finally:
245245
conn.close()
246+
247+
248+
def test_psycopg2_cypher_with_array_parameter_in_clause():
249+
"""Test Cypher query with array parameter using IN clause - reproduces the ClassCastException issue"""
250+
params = get_connection_params(arcadedb)
251+
conn = psycopg.connect(**params)
252+
conn.autocommit = True
253+
254+
try:
255+
with conn.cursor() as cursor:
256+
# Create test vertices first
257+
cursor.execute('{cypher} CREATE (n:CHUNK {text: "chunk1"}) RETURN ID(n)')
258+
rid1 = cursor.fetchone()[0]
259+
cursor.execute('{cypher} CREATE (n:CHUNK {text: "chunk2"}) RETURN ID(n)')
260+
rid2 = cursor.fetchone()[0]
261+
cursor.execute('{cypher} CREATE (n:CHUNK {text: "chunk3"}) RETURN ID(n)')
262+
rid3 = cursor.fetchone()[0]
263+
264+
# Now query with IN clause using array parameter
265+
rids_list = [rid1, rid2, rid3]
266+
query_params = {'ids': rids_list}
267+
cursor.execute('{cypher} MATCH (n:CHUNK) WHERE ID(n) IN %(ids)s RETURN n.text as text, ID(n) as id', query_params)
268+
269+
results = cursor.fetchall()
270+
assert len(results) == 3
271+
272+
# Verify we got all three chunks
273+
texts = [r[0] for r in results]
274+
assert 'chunk1' in texts
275+
assert 'chunk2' in texts
276+
assert 'chunk3' in texts
277+
finally:
278+
conn.close()

postgresw/src/main/java/com/arcadedb/postgres/PostgresType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ public static Object deserialize(final long code, final int formatCode, final by
339339
private static Object deserializeText(final long code, final byte[] valueAsBytes) {
340340
String str = new String(valueAsBytes, DatabaseFactory.getDefaultCharset());
341341
if (code == 0) { // UNSPECIFIED
342+
// Try to detect if this is a PostgreSQL array format
343+
if (str.startsWith("{") && str.endsWith("}")) {
344+
// Parse as an array using the TEXT array parser
345+
return parseArrayFromString(str, s -> s);
346+
}
342347
return str;
343348
}
344349

postgresw/src/test/java/com/arcadedb/postgres/PostgresWJdbcIT.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,4 +656,45 @@ void createVertexCypherQueryParams() throws Exception {
656656
}
657657
}
658658
}
659+
660+
@Disabled("Pending fix verification")
661+
@Test
662+
void testCypherWithArrayParameterInClause() throws Exception {
663+
try (var conn = getConnection()) {
664+
try (var st = conn.createStatement()) {
665+
st.execute("create vertex type CHUNK");
666+
// Create test vertices directly
667+
st.execute("{cypher} CREATE (n:CHUNK {text: 'chunk1'})");
668+
st.execute("{cypher} CREATE (n:CHUNK {text: 'chunk2'})");
669+
st.execute("{cypher} CREATE (n:CHUNK {text: 'chunk3'})");
670+
}
671+
672+
// Get all RIDs
673+
String[] rids = new String[3];
674+
try (var st = conn.createStatement()) {
675+
try (var rs = st.executeQuery("{cypher} MATCH (n:CHUNK) RETURN ID(n) ORDER BY n.text")) {
676+
int idx = 0;
677+
while (rs.next() && idx < 3) {
678+
rids[idx++] = rs.getString(1);
679+
}
680+
}
681+
}
682+
683+
// Now query with IN clause using array parameter - this should reproduce the ClassCastException
684+
try (var pst = conn.prepareStatement("{cypher} MATCH (n:CHUNK) WHERE ID(n) IN ? RETURN n.text as text ORDER BY n.text")) {
685+
Array array = conn.createArrayOf("text", rids);
686+
pst.setArray(1, array);
687+
688+
try (var rs = pst.executeQuery()) {
689+
assertThat(rs.next()).isTrue();
690+
assertThat(rs.getString("text")).isEqualTo("chunk1");
691+
assertThat(rs.next()).isTrue();
692+
assertThat(rs.getString("text")).isEqualTo("chunk2");
693+
assertThat(rs.next()).isTrue();
694+
assertThat(rs.getString("text")).isEqualTo("chunk3");
695+
assertThat(rs.next()).isFalse();
696+
}
697+
}
698+
}
699+
}
659700
}

0 commit comments

Comments
 (0)