From 156eef4c04715b7a574d89de637b1ef8f53c97aa Mon Sep 17 00:00:00 2001 From: Evgen Bielozorov Date: Wed, 29 Jul 2026 15:38:01 +0200 Subject: [PATCH 1/2] fix: raise clear error for on-disk (splayed) table reference Reading a splayed/partitioned table (e.g. `get `:test/ set ([] a:til 10)`) crashed the client with an opaque "ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.Object;". kdb+ transmits such a table as table(98) -> dict(99) whose value is the on-disk path symbol (`:test/`, type -11) rather than the column data, so the Flip(Dict) constructor's (Object[]) dict.y cast fails. The column data is not present in the message and cannot be materialized on the Java side. Detect this case in Flip(Dict) and throw a descriptive IllegalArgumentException that names the referenced path and explains how to materialize the table in kdb+ before sending it. Add a unit test built from the exact IPC bytes reported in issue #51. --- javakdb/src/main/java/com/kx/c.java | 8 ++++++++ javakdb/src/test/java/com/kx/CTest.java | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/javakdb/src/main/java/com/kx/c.java b/javakdb/src/main/java/com/kx/c.java index 79374c1..7d2dc22 100644 --- a/javakdb/src/main/java/com/kx/c.java +++ b/javakdb/src/main/java/com/kx/c.java @@ -634,9 +634,17 @@ public static class Flip{ * Create a Flip (KDB+ table) from the values stored in a Dict. * @param dict Values stored in the dict should be an array of Strings for the column names (keys), with an * array of arrays for the column values + * @throws IllegalArgumentException if the dict values are not an array of column values. This happens when a + * splayed/partitioned table stored on disk is received: kdb+ transmits it as a mapping of column names to a + * file path (a symbol) rather than the column data itself, so it cannot be represented as an in-memory Flip. + * @see Splayed tables */ public Flip(Dict dict){ x=(String[])dict.x; + if(!(dict.y instanceof Object[])) + throw new IllegalArgumentException("Cannot create a Flip from a table whose column data is stored on disk" + +" (splayed/partitioned table); kdb+ sent a reference to \""+dict.y+"\" instead of the column values." + +" Materialize the table in kdb+ before sending it, e.g. `select from t` or `value flip t`."); y=(Object[])dict.y; } /** diff --git a/javakdb/src/test/java/com/kx/CTest.java b/javakdb/src/test/java/com/kx/CTest.java index 7660d4f..60332c6 100644 --- a/javakdb/src/test/java/com/kx/CTest.java +++ b/javakdb/src/test/java/com/kx/CTest.java @@ -931,6 +931,26 @@ public void testDeserializeEmptyTable() } } + @Test + public void testDeserializeSplayedTableReference() + { + // response from executing 'get `:test/ set ([] a:til 10)' + // A splayed (on-disk) table is transmitted as table(98) -> dict(99) whose value is the + // file-path symbol `:test/ (type -11) rather than the column data, so it cannot be + // represented as an in-memory Flip. This should surface as a clear IllegalArgumentException + // rather than an opaque ClassCastException (issue #51). + byte[] buff = {(byte)0x01, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x1b, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x62, (byte)0x00, (byte)0x63, (byte)0x0b, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0xf5, (byte)0x3a, (byte)0x74, (byte)0x65, (byte)0x73, (byte)0x74, (byte)0x2f, (byte)0x00}; + com.kx.c c=new com.kx.c(); + try { + c.deserialize(buff); + Assert.fail("Expected an IllegalArgumentException to be thrown"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains(":test/")); + } catch (Exception e) { + Assert.fail(e.toString()); + } + } + @Test public void testMonthToString() { From fadbed5cdecb37f4fba1281629e5b50459861ade Mon Sep 17 00:00:00 2001 From: Evgen Bielozorov Date: Fri, 31 Jul 2026 13:07:59 +0200 Subject: [PATCH 2/2] refactor: make Flip(Dict) error describe the condition, not assume splay Address review feedback on PR #104: the guard fires whenever the dict values are not an Object[] of columns, which can also result from a mis-constructed Dict, not only a splayed/on-disk table. Reword the message to lead with the actual condition and present both causes, so it does not mislead a caller whose own code produced the bad Dict. Add a test for the mis-constructed-Dict case. --- javakdb/src/main/java/com/kx/c.java | 14 ++++++++------ javakdb/src/test/java/com/kx/CTest.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/javakdb/src/main/java/com/kx/c.java b/javakdb/src/main/java/com/kx/c.java index 7d2dc22..7ca587d 100644 --- a/javakdb/src/main/java/com/kx/c.java +++ b/javakdb/src/main/java/com/kx/c.java @@ -634,17 +634,19 @@ public static class Flip{ * Create a Flip (KDB+ table) from the values stored in a Dict. * @param dict Values stored in the dict should be an array of Strings for the column names (keys), with an * array of arrays for the column values - * @throws IllegalArgumentException if the dict values are not an array of column values. This happens when a - * splayed/partitioned table stored on disk is received: kdb+ transmits it as a mapping of column names to a - * file path (a symbol) rather than the column data itself, so it cannot be represented as an in-memory Flip. + * @throws IllegalArgumentException if the dict values are not an array of column data (Object[]). This can be + * the case when a splayed/partitioned table stored on disk is received (kdb+ transmits a mapping of column names + * to an on-disk path symbol rather than the column data), or when the Dict was constructed with values that are + * not column arrays. * @see Splayed tables */ public Flip(Dict dict){ x=(String[])dict.x; if(!(dict.y instanceof Object[])) - throw new IllegalArgumentException("Cannot create a Flip from a table whose column data is stored on disk" - +" (splayed/partitioned table); kdb+ sent a reference to \""+dict.y+"\" instead of the column values." - +" Materialize the table in kdb+ before sending it, e.g. `select from t` or `value flip t`."); + throw new IllegalArgumentException("A Flip requires the dictionary values to be an array of column data" + +" (Object[]) but received: "+dict.y+". This can occur when a splayed/partitioned table stored on disk is" + +" received (kdb+ transmits an on-disk path reference rather than the columns), or when the Dict was" + +" constructed with values that are not column arrays."); y=(Object[])dict.y; } /** diff --git a/javakdb/src/test/java/com/kx/CTest.java b/javakdb/src/test/java/com/kx/CTest.java index 60332c6..df17d6d 100644 --- a/javakdb/src/test/java/com/kx/CTest.java +++ b/javakdb/src/test/java/com/kx/CTest.java @@ -951,6 +951,21 @@ public void testDeserializeSplayedTableReference() } } + @Test + public void testFlipFromDictWithNonColumnValues() + { + // The same guard also protects against a Dict that was simply constructed with values that are + // not column arrays (i.e. a caller bug, not a splayed table); the message describes the actual + // condition rather than assuming a specific cause. + c.Dict dict = new c.Dict(new String[]{"a"}, "not-a-column-list"); + try { + new c.Flip(dict); + Assert.fail("Expected an IllegalArgumentException to be thrown"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("Object[]")); + } + } + @Test public void testMonthToString() {