KxSystems / javakdb

Using Java with kdb+
https://code.kx.com/q/interfaces
Apache License 2.0
54 stars 45 forks source link

ClassCastException on reading splay table #51

Open dzmipt opened 3 years ago

dzmipt commented 3 years ago

The bug is inside c.java which parses the result from kdb.

If you run GridViewer with the following query

String query ="get `:test/ set ([] a:til 10)";

then you get the following exception

Dec 07, 2020 10:04:00 PM kx.examples.GridViewer main
SEVERE: null
java.lang.ClassCastException: java.lang.String cannot be cast to [Ljava.lang.Object;
    at kx.c$Flip.<init>(c.java:479)
    at kx.c.r(c.java:977)
    at kx.c.deserialize(c.java:1395)
    at kx.c.readMsg(c.java:1522)
    at kx.c.k(c.java:1602)
    at kx.c.k(c.java:1617)
    at kx.examples.GridViewer.main(GridViewer.java:48)
dzmipt commented 3 years ago

In reality, kdb doesn't return content of the table (btw, I am playing with 3.6 - interesting how other version behaviours). Here is what the server returns from another q console

q)h:hopen 5001
q)h"tables[]"
,`test1
q)t:h"test1"
q)t
+`a`b!`:test1/

So the Flip has column names and a symbol for the table. A different q console, also get crazy from such object:

q)meta t
'test1/a. OS reports: No such file or directory
  [0]  meta t
       ^
q))count t
'pn
  [4]  count t
       ^

I think that either c() should returns some special c.SpecialFlip class. Another option is to correctly read the object into c.Flip but gives user ability to understand that content couldn't be retrieved and also reference to the symbolic name.

sshanks-kx commented 3 years ago

With debugging:

get `:test/ set ([] a:til 10) Current decoding thinks data contains type 98, which contains type 99 (which has type 11 & type -11). The -11 appears to be set to ":test/".

([]a:10?100) Current decoding thinks data contains type 98, which contains type 98, contains type 99 (which has type 11 & type 0 and 7)

Investigating further ...

sshanks-kx commented 3 years ago

Example of unit test that can be added to recreate (note: doesn't yet have intended action as change required, but allows quick testing)

    @Test
    public void testSplayResponse()
    {
        // response from executing 'get `:test/ set ([] a:til 10)'
        // "\x01\x02\x00\x00\x1b\x00\x00\x00\x62\x00\x63\x0b\x00\x01\x00\x00\x00\x61\x00\xf5\x3a\x74\x65\x73\x74\x2f\x00"
        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};
        kx.c c=new kx.c();
        try{
            Object res = c.deserialize(buff);
        } catch (Exception e) {
            Assert.fail(e.toString());
        }
    }

Running mvn -Dtest=cTest#testSplayResponse test

Example of using KDB+ directly as a client were it doesn't have access to 'test' dir containing table dir (i.e. ran on same host but from different dir from KDB+ server)

q -p 5001 (server)

client instance

q)h:hopen `::5001
q)h"get `:test/ set ([] a:til 10)"
+(,`a)!`:test/
q)a:h"get `:test/ set ([] a:til 10)"
q)type a
98h
q)a[`a]
'test/a. OS reports: No such file or directory
  [0]  a[`a]

Will look further at potential changes that could be made & update.