IPC

File I/O

Console I/O


            0 "this goes to console"
            1 "this goes to stdout"
            2 "this goes to stderr"
        
If you use negative handlers, it means asynchronous write. This will add new line characters to new lines.
To write a file to console I/O, do something like \1 txt_for_stdout.txt

File paths

File paths in KDB start with `:.
You can use hsym to turn a symbol/string into a file path, like this: hsym "my/file/path.
You can use sv to construct file paths from a list of folders, like this: ` sv `:[list of folders].
To list all files under a path, do this key `:.

File I/O

KDB files


            // write to KDB file
            `:filepath set tbl
            // read from KDB file
            get `:filepath
        
If this KDB file is a flat table, you will be able to edit it directly on disk using qsql.
For example, something like select from `:filepath where sym=`AAPL

TXT files


            // write to txt
            fileHandle: hopen `:tbl.txt
            fileHandle "writing some text without starting a new line"
            neg[fileHandle] "starting a new line"
            hclose fileHandle
            // or use 0:
            `:tbl.txt 0:("hello";"goodbye") 
            // read from txt
            read0 `:tbl.txt
        

CSV files


            // write to csv
            save `:tbl.csv
            // read from csv
            // (type; delimiter) 0: `:tbl.csv
            ("SJF"; enlist ",") 0: `:tbl.csv
        
Type is a string that specifies the data type of each column, SJF here means symbol, long, float.
Type must be provided.

JSON files


            // write to json is the same as csv
            save `:tbl.json
            // read from json
            load `:tbl.json
        

Summary

In KDB, there are actually several pairs of I/O.
  1. High level abstraction: save and load
    This pair can deal with almost all file formats, but they are less flexible. For example, you can't change the file name when you save/load. Neither can you manage compression level.
  2. Deal with tables: set, get
    This pair is used to store and retrieve tables. You have the freedom to choose file name and compression level.
  3. Deal with text files and csvs: 0:, read0
    This pair is used to read and write text files or csvs. They don't require the file to be structured like a table.
  4. Deal with bytes: 1:, read1
    This pair is used to read and write bytes.
    q)`:test.txt 0:("hello";"goodbye")      // write some text to a file
    q)read1`:test.txt                       // read in as bytes
    0x68656c6c6f0a676f6f646279650a
    q)"c"$read1`:test.txt                   // convert from bytes to char
    "hello\ngoodbye\n"
  5. Pipes: hopen, hclose
In fact, the higher level functions are just wrappers around the lower level functions. For example, save is just a wrapper around set. And as you see in the example above, read0 is just a wrapper around read1 with some extra parsing.

IPC

Basic client and server

Sync flush: the server will receive stuff you send one by one

            // on the server side
            q) h:hopen `::portnumber
            // on the client side
            q) h:hopen `:localhost:portnumber
            q) result:h("select from tablename where condition") // query some data
            q) hclose h
        
Async flush: the server will receive all the stuff you send

            q) neg[h][]     // this blocks the client until it receives something from the server
            // alternatively
            q) neg[h](::)   // doesn't block client; client can send h "" to ask the server for confirmation
        

Advanced IPC using .z namespace

On the client side, you can't do much except those mentioned above.
But on the server side, .z namespace can help you achieve a lot more functionalities.

talk to client process

You can check the ip address and the user of the calling process using .z.a and .z.u.
To talk to the client, you can open a handle to it using .z.w, and with this handle, you can send messages back to the calling process, like how you send messages the other way around using h.

These three functions are defined by KDB, you don't need to define their behaviors.
In the following sections, the functions act as event handlers. You need to define their behaviors when certain events happen.
You can reset the behaviors of those event handlers using \x such as \x .z.pw

Set up permissions

.z.pw is triggered when a client process tries to establish a new connection with the server.
A common pattern to use permissions is:

            // on the server side, define .z.pw
            q) .z.pw:{[myun; mypw] 1b}
            // on the client side, connect with username and password
            q) h:hopen `:host:port:myun:mypw
        
Or you can add username and password pairs to a table.

Monitor current connections

.z.po is triggered when a new connection is open.
.z.pc is triggered when a connection is closed.
Both of them by default take one argument, and you can define them like so:

            q) .z.po:{0N!"Connection open on ", string[x]}
            q) .z.pc:{0N!"Connection closed on ", string[x]}
        
Again, you can define the functions to add/remove an entry to a table of active connections.

Message handlers

.z.pg is triggered when server receives a synchronous message from the client.
.z.ps is triggered when server receives an asynchronous message from the client.
.z.ph is the http alternative for .z.pg and .z.ps.
One usecase is to decide who has permission to run which function.

            q) .z.pg{[query]
                class: .perm.users[.z.u][`class];
                $[class~`superuser; value query; \"Permission denied\"]
            }