kxkdb
Kdb+ interface for the Rust programming language.
The interface comprises two features:
- IPC: Connecting Rust and kdb+ processes via IPC
- API: Embedding Rust code inside kdb+ processes
Documentation
Documentation for this interface can be found at https://docs.rs/kxkdb/.
Kdbplus
The kxkdb interface is forked from the excellent kdbplus interface, developed by diamondrod.
IPC
The IPC feature enables qipc communication between Rust and kdb+.
Connectivity is via TCP or Unix Domain Sockets, with support for both compression and TLS encryption of messages.
Connection and listener methods are provided, enabling development of both
- Rust IPC clients of kdb+ server processes
- Rust IPC servers of kdb+ client processes
Installation
Add kxkdb as a dependency, with feature ipc.
You may also want to add an asynchronous runtime such as Tokio.
e.g.
[]
= { = "0.0", = ["ipc"] }
= { = "1.24", = ["full"] }
Examples
Client
use *;
use qattribute;
async
Server
Setup a credentials file containing usernames and (SHA-1 encrypted) passwords.
e.g.
$ cat userpass.txt
fred:e962cde7053eed120f928cd18e58ebd31be77543
homer:df43ad44d44e898f8f4e6ed91e6952bfce573e12
Note: Hashed passwords can be generated in q using .Q.sha1.
Store the path of this file in environment variable KDBPLUS_ACCOUNT_FILE.
e.g.
$ export KDBPLUS_ACCOUNT_FILE=`pwd`/userpass.txt
The following code will establish a Rust server process, listening on port 4321.
use *;
async
A kdb+ client can then connect using the correct credentials.
e.g.
q)hopen`:127.0.0.1:4321:fred:flintstone;
"Hello"
Type Mapping
The following table displays the input types used to construct different q types (implemented as K objects).
| q | Rust |
|---|---|
boolean |
bool |
guid |
[u8; 16] |
byte |
u8 |
short |
i16 |
int |
i32 |
long |
i64 |
real |
f32 |
float |
f64 |
char |
char |
symbol |
String |
timestamp |
chrono::DateTime<Utc> |
month |
chrono::NaiveDate |
date |
chrono::NaiveDate |
datetime |
chrono::DateTime<Utc> |
timespan |
chrono::Duration |
minute |
chrono::Duration |
second |
chrono::Duration |
time |
chrono::Duration |
list |
Vec<T> (T a corresponding type above) |
compound list |
Vec<K> |
table |
Vec<K> |
dictionary |
Vec<K> |
generic null |
() |
Note: The input type can differ from the inner type. For example, timestamp has an input type of chrono::DateTime<Utc> but the inner type is i64, denoting an elapsed time in nanoseconds since 2000.01.01D00:00:00.
Environment Variables
KDBPLUS_ACCOUNT_FILE
Path to a credential file, used by a Rust server to manage access from kdb+ clients.
Contains a user name and SHA-1 hashed password on each line, delimited by ':'.
KDBPLUS_TLS_KEY_FILE
The path to a pkcs12 file used for TLS connections.
KDBPLUS_TLS_KEY_FILE_SECRET
The password for the above pkcs12 file.
QUDSPATH
Defines the (real or abstract) path used for Unix Domain Sockets to $QUDSPATH/kx.[PORT].
n.b. If not defined, this will default to /tmp/kx.[PORT]
API
The API feature enables the development of shared object libraries in Rust, which can be dynamically loaded into kdb+.
In order to avoid large unsafe blocks, most native C API functions are provided with a wrapper funtion and with intuitive implementation as a trait method. The exceptions are variadic functions knk and k, which are provided under native namespace with the other C API functions.
Installation
Add kxkdb as a dependency, with feature api.
[]
={="0.0", =["api"]}
Examples
C API Style
use qtype;
use *;
use *;
pub extern "C"
pub extern "C"
pub extern "C"
A kdb+ process can then dynamically load and call these functions as follows:
q)summon:`libc_api_examples 2: (`create_symbol_list; 1)
q)summon[]
`Abraham`Isaac`Jacob`Joseph
q)`Abraham`Isaac`Jacob`Joseph ~ summon[]
q)catchy: `libc_api_examples 2: (`catchy; 2);
q)catchy[$; ("J"; "42")]
42
q)catchy[+; (1; `a)]
error: type
q)behold: `libc_api_examples 2: (`dictionary_list_to_table; 1);
q)behold[]
a b
------
0 0
10 100
20 200
Rust Style
The examples below are written without unsafe code.
use qtype;
use *;
use *;
pub extern "C"
pub extern "C"
And q code is here:
q)summon:`libc_api_examples 2: (`create_symbol_list2; 1)
q)summon[]
`Abraham`Isaac`Jacob`Joseph
q)chill: `libc_api_examples 2: (`no_panick; 2);
q)chill[$; ("J"; "42")]
success!
42
q)chill[+; (1; `a)]
FYI: type
q)climate_change: libc_api_examples 2: (`create_table2; 1);
q)climate_change[]
time temperature
-----------------------------------------
2003.10.10D02:24:19.167018272 22.1
2006.05.24D06:16:49.419710368 24.7
2008.08.12D23:12:24.018691392 30.5
Test
Testing is conducted in two ways:
- Using cargo
- Running a q test script
1. Using Cargo
Before starting the test, start a kdb+ process listening on port 5000.
)
Then run the test:
Note: Currently 20 tests fails for api examples in document. This is because the examples do not have main function by nature of api but still use #[macro_use].
2. Running a q Test Script
Tests are conducted with tests/test.q by loading the example functions built in api_examples.
;
)