How can I recompose a Q function call as a string from a list of arguments?

Viewed 262

I am using C# to execute q code against a KDB database using the c.cs library provided by KX. I provide arguments in the form of objects (integers, strings, Dict instances etc.) to the c.k method in that library, and this all works fine. However, when I have a query that doesn't work using this method, invariably what I want to do is take the query I've just executed through the C# adapter and re-execute it in a Q console so that I can tinker with it and work out what I did wrong. The problem is, in the C# environment what I have as arguments to my Q function (in some cases somewhat complicated dictionary-based trees of arguments), It's a real pain to manually convert my arguments into the func[arg1;arg2;arg3...]; format expected by the Q console. What I'd really like to be able to do is pass my arguments to a function that would recompose the original query in a syntax that would enable me to easily re-execute the original query in the console. Something like this:

In C#

var client = new c();
[connect]
var funcName = "myFunc";
var arg1 = "test";
var arg2 = 12345;
var arg3 = new c.Dict(new [] { "a", "b" }, new [] { true, false });
try {
  var result = c.k(funcName, arg1, arg2, arg3);
} catch {
  var composedStatement = c.k("getComposedStatement", funcName, new[] { arg1, arg2, arg3 });
  // composedStatement: "myFunc[`test;12345i;`a`b!10b];"
  Logger.LogError("KDB Query failed: " + composedStatement);
}

In Q

getComposedStatement:{[funcName;arguments]
  :(string funcName),"[",(qWizardryGoesHere arguments),"];";
  }

Hopefully that's clear what I'm trying to achieve. The important thing is that the getComposedStatement function takes a function name, and an arbitrary number of arguments that could be of any type, including nested types (lists of lists, list of dictionary, dictionary of different types etc.) and returns a statement that can be directly copied and pasted into a Q console for execution.

1 Answers

I think this is what you are looking in a kdb function.

Using .Q.s1 to get the string representation for each parameter and then sv to get ; separated string for the parameters. There is also some extra bits to create a . / @ apply:

getComposedStatement:{
    // conditional to create a . or @ apply depending on number of parameters
    // @ apply will only work with 1 parameter 
    $[1 < count y;".";"@"],
       "[`",x,";(",(";" sv .Q.s1 each y),")]"
}

Useful links:

https://code.kx.com/q/ref/dotq/#qs1-string-representation

https://code.kx.com/q/ref/apply/

https://code.kx.com/q/ref/sv/

Edit: A better approach to help you debug in q would be to avoid the string based solution entirely and create a debug variable. You could then have the function and params already available in the q console which can be ran with eval .debug

createDebug:{
    // simply create a list with function name and parameters
    // assign it to the .debug namespace so it is available 
    // outside of the function call.
    .debug:(,/)enlist each x,y;

    }

testFunc:{1 2 3!(x;y;z)}

createDebug[`testFunc;("test";12345i;`a`b!10b)]
show .debug
(`testFunc;"test";12345;(`a`b)!10b)

eval .debug
key value
1   "test"
2   12345
3   (`a`b)!10b

Edit 2:

Since OP is having issues with objects greater than 2000 characters, another way to do this would be to make use of JSON. I'm not to sure how this will be handled in C# but it's worth a try.

getComposedStatement:{
    
    (x;.j.j each y)    

    }
    

// 3rd arg is a very large dictionary
getComposedStatement[`testFunc;("test";12345i;(til 2000)!2000?`3)]

(`testFunc;(""test"";"12345";{"0":"gia","1":"dmj","2":"pnh","3":"ofc","4":"hon","5":"jjb", ...

You will then have to use a run function on the other side to convert the json back:

runComposedStatement:{
    
    eval (first x),.j.k each last x    

}

runComposedStatement[getComposedStatement[`testFunc;("test";12345i;(til 2000)!2000?`3)]]

(1j, 2j, 3j)!("test";12345f;(`0`1`2`3`4`5`6`7`8`9`10`11`12`13`14`15 ...

I don't want to post the entire dictionary but I verified it was complete. Hopefully that can achieve what you want or at least one step closer. May need more to handle the JSON.

Related