Search code examples
kdb

Exception/Error Handling in q kdb -- Alternative of Try-Catch-Finally(Java)/Try-Except-Finally(Python)


As we have try-catch in java, I can find trap-at in q kdb.
But my requirement is of try-catch-finally i.e in try block I'm opening odbc connection and in finally I want to close odbc connection.

Sudo code:

try{
  con=openODBCConnection(dbName);
  //n statements;
}catch(Exception e){
   catchingNotSoImpException(e)
}finally{
 closeODBCCon(con);
}

Solution

  • This is a fairly generic approach to using try-catch-finally logic in kdb, which separates out the try-catch, always runs the "finally" function. This returns the output if successful in either the try or the catch if necessary, or the error code if both try and catch have broken, allowing (potentially) more useful investigation and/or security on breaks:

    tcf:{[t;c;f] 
        r:@[(1b;)value@;t;@[(1b;)c@;;(0b;)::]];
        f[]; 
        $[r 0;r 1;'`$r 1]}
    

    The top line is the "try-catch" portion.

    @[(1b;)value@;t;@[(1b;)c@;;(0b;)::]]
    

    The function is called like so:

    tcf[(tryfunc;1;`a);catchfunc;finallyfunc]
    

    so the input has to be something that can be value'd in kdb - symbol, function and argument list, or string.

    This can be used as-is, but for an explanation:

    The key portion of logic here are the projections of (1b;) and (0b;) together with @ on the value or c functions tell that operation to wait on input - so within the first part:

    (1b;)value@
    

    waits for the input t - if the value operation succeeds, (1b;correctOutput) is returned, i.e. the projection is executed.

    If that fails, the error is passed to

    @[(1b;)c@;;(0b;)::]
    

    Which is basically the same thing, but instead of value, it uses the catch function, c. This is a projection which takes the input passed from the failed value before, then applies the same operations as above. Failed output is passed to a global null ::.

    This ensures the data structure r has a leading 1b if either the try or catch was successful, and a 0b if both failed.

    The finally function is then run, and the return is either the successful answer or a thrown error in the case of double failure.

    Examples using similar definitions to Rahul's answer:

    q)tryfunc
    {x+y}
    q)workingcatchfunc
    {show "catch";100}
    q)brokencatchfunc
    {show "catch";1+`a}
    q)finallyfunc
    {show"finally"}
    q)tcf[(tryfunc;1;1);workingcatchfunc;finallyfunc]
    "finally"
    2
    q)tcf[(tryfunc;1;`a);workingcatchfunc;finallyfunc]
    "catch"
    "finally"
    100
    q)tcf[(tryfunc;1;`a);brokencatchfunc;finallyfunc]
    "catch"
    "finally"
    'type
    

    This works with functions taking any number of arguments also:

    q)tcf[(monot;1);brokencatchfunc;finallyfunc]
    "finally"
    10
    q)tcf[(monot;`);brokencatchfunc;finallyfunc]
    "catch"
    "finally"
    'type