KEMI InterpretersLink
The following KEMI scripting languages can be used to write SIP routing logic for Kamailio:
- JavaScript
- Lua
- Python
- Python3
- Ruby
- Squirrel
A configuration file for Kamailio that uses a KEMI scripting language has the global parameters, loading modules and
module parameters in the native scripting and sets the value of cfgengine
to the KEMI scripting language identifier.
The KEMI scripting language identifiers are:
jsdt
- for JavaScriptlua
- for Luapython
- for Pythonpython3
- for Python3ruby
- for Rubysqlang
- for Squirrel
JavaScript KEMI InterpreterLink
It is implemented by app_jsdt
module. The JavaScript interpreter is imported inside the module from
DukTape project, therefore it doesn't require to install any external libraries.
To use it, set inside kamailio.cfg
:
loadmodule "app_jsdt.so"
modparam("app_jsdt", "load", "/path/to/script.js")
cfgengine "jsdt"
Inside the JavaScript script, following functions have a predefined role:
ksr_request_route()
- is executed by Kamailio core every time a SIP request is received. If this function is not defined, then Kamailio will write error messages. This is equivalent ofrequest_route {}
fromkamailio.cfg
.ksr_reply_route()
- is executed by Kamailio core every time a SIP Response (reply) is received. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofreply_route {}
fromkamailio.cfg
.ksr_onsend_route()
- is executed by Kamailio core every time a SIP request (and optionally for a response) is sent out. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofonsend_route {}
fromkamailio.cfg
.branch route callback
- the name of the JavaScript function to be executed instead of a branch route has to be provided as parameter toKSR.tm.t_on_branch(…)
onreply route callback
- the name of the JavaScript function to be executed instead of an onreply route has to be provided as parameter toKSR.tm.t_on_reply(…)
failure route callback
- the name of the JavaScript function to be executed instead of a failure route has to be provided as parameter toKSR.tm.t_on_failure(…)
branch failure route callback
- the name of the JavaScript function to be executed instead of anevent route
for branch failure has to be provided as parameter toKSR.tm.t_on_branch_failure(…)
event route callback
- the name of the JavaScript function to be exectued instead of module specificevent_route
blocks is provided viaevent_callback
parameter of that module
A complete example of using JavaScript as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGJSDT
The file kamailio-basic-kemi-jsdt.js
has to be saved to local disk and the load
parameter for app_jsdt
module
inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_jsdk
is available at:
Lua KEMI InterpreterLink
It is implemented by app_lua
module. The Lua interpreter is linked from liblua
library, supported Lua versions:
5.1 and 5.2.
loadmodule "app_lua.so"
modparam("app_lua", "load", "/path/to/script.lua")
cfgengine "lua"
Inside the Lua script, following functions have a predefined role:
ksr_request_route()
- is executed by Kamailio core every time a SIP request is received. If this function is not defined, then Kamailio will write error messages. This is equivalent ofrequest_route {}
fromkamailio.cfg
.ksr_reply_route()
- is executed by Kamailio core every time a SIP Response (reply) is received. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofreply_route {}
fromkamailio.cfg
.ksr_onsend_route()
- is executed by Kamailio core every time a SIP request (and optionally for a response) is sent out. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofonsend_route {}
fromkamailio.cfg
.branch route callback
- the name of the Lua function to be executed instead of a branch route has to be provided as parameter toKSR.tm.t_on_branch(…)
onreply route callback
- the name of the Lua function to be executed instead of an onreply route has to be provided as parameter toKSR.tm.t_on_reply(…)
failure route callback
- the name of the Lua function to be executed instead of a failure route has to be provided as parameter toKSR.tm.t_on_failure(…)
branch failure route callback
- the name of the Lua function to be executed instead of anevent route
for branch failure has to be provided as parameter toKSR.tm.t_on_branch_failure(…)
event route callback
- the name of the Lua function to be exectued instead of module specificevent_route
blocks is provided viaevent_callback
parameter of that module
Note: besides the new KEMI Lua KSR module, the old sr
Lua module exported by app_lua
is still available.
A complete example of using Lua as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGLUA
The file kamailio-basic-kemi-lua.lua
has to be saved to local disk and the load
parameter for app_lua
module
inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_lua
is available at:
Basic KEMI Lua Scripting ExampleLink
The file kamailio.cfg
with global parameters and module settings:
#!KAMAILIO
####### Global Parameters #########
debug=3
log_stderror=yes
fork=yes
children=2
memdbg=5
memlog=5
auto_aliases=no
listen=udp:127.0.0.1:5060
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "xlog.so"
loadmodule "ctl.so"
loadmodule "debugger.so"
loadmodule "app_lua.so"
# ----------------- setting module-specific parameters ---------------
# ----- jsonrpcs params -----
modparam("jsonrpcs", "pretty_format", 1)
# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
####### Routing Logic ########
modparam("app_lua", "load", "/etc/kamailio/kamailio.lua")
cfgengine "lua"
The file /etc/kamailio/kamailio.lua
with the routing logic for runtime:
-- Kamailio - equivalent of routing blocks in Lua
-- KSR - the new dynamic object exporting Kamailio functions
-- sr - the old static object exporting Kamailio functions
--
-- SIP request routing
-- equivalent of request_route{}
function ksr_request_route()
KSR.info("===== request - from kamailio lua script\n");
if KSR.maxfwd.process_maxfwd(10) < 0 then
KSR.sl.send_reply(483, "Too Many Hops");
return;
end
-- KSR.sl.sreply(200, "OK Lua");
KSR.pv.sets("$du", "sip:127.0.0.1:5080")
KSR.tm.t_on_branch("ksr_branch_route_one");
KSR.tm.t_on_reply("ksr_onreply_route_one");
KSR.tm.t_on_failure("ksr_failure_route_one");
if KSR.tm.t_relay() < 0 then
KSR.sl.send_reply(500, "Server error")
end
end
-- SIP response routing
-- equivalent of reply_route{}
function ksr_reply_route()
KSR.info("===== response - from kamailio lua script\n");
end
-- branch route callback
-- equivalent of a branch_route{}
function ksr_branch_route_one()
KSR.info("===== branch route - from kamailio lua script\n");
end
-- onreply route callback
-- equivalent of an onreply_route{}
function ksr_onreply_route_one()
KSR.info("===== onreply route - from kamailio lua script\n");
end
-- failure route callback
-- equivalent of a failure_route{}
function ksr_failure_route_one()
KSR.info("===== failure route - from kamailio lua script\n");
end
Python KEMI InterpreterLink
It is implemented by app_python
module. The Python interpreter is linked from libpython
, supported Python versions:
2.5, 2.6 and 3.x (via app_python3).
loadmodule "app_python.so"
modparam("app_python", "script_name", "/path/to/script.py")
cfgengine "python"
In the Python script you have to declare the global mod_init()
method where to instantiate an object of a class that
implements the other callback methods (functions) to be executed by Kamailio.
Inside the new class, the following methods are relevant:
ksr_request_route(self, msg)
- is executed by Kamailio core every time a SIP request is received. If this function is not defined, then Kamailio will write error messages. This is equivalent ofrequest_route {}
fromkamailio.cfg
.ksr_reply_route(self, msg)
- is executed by Kamailio core every time a SIP Response (reply) is received. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofreply_route {}
fromkamailio.cfg
.ksr_onsend_route(self, msg)
- is executed by Kamailio core every time a SIP request (and optionally for a response) is sent out. If this function is not defined, then Kamailio will not write error messages. This is equivalent ofonsend_route {}
fromkamailio.cfg
.branch route callback
- the name of the Python function to be executed instead of a branch route has to be provided as parameterto KSR.tm.t_on_branch(…)
onreply route callback
- the name of the Python function to be executed instead of an onreply route has to be provided as parameter toKSR.tm.t_on_reply(…)
failure route callback
- the name of the Python function to be executed instead of a failure route has to be provided as parameter toKSR.tm.t_on_failure(…)
branch failure route callback
- the name of the Python function to be executed instead of an event route for branch failure has to be provided as parameter toKSR.tm.t_on_branch_failure(…)
event route callback
- the name of the Python function to be exectued instead of module specificevent_route
blocks is provided viaevent_callback
parameter of that module
Note: besides the new KEMI Python KSR module, the old Router
Python module exported by app_python
is still
available.
A complete example of using Python as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGPYTHON
The file kamailio-basic-kemi-python.py
has to be saved to local disk and the load
parameter for
app_python
module inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_python
is available at:
Basic KEMI Python Scripting ExampleLink
The file kamailio.cfg
with the global parameters and modules settings:
#!KAMAILIO
####### Global Parameters #########
debug=4
log_stderror=yes
fork=yes
children=2
memdbg=5
memlog=5
auto_aliases=no
listen=udp:127.0.0.1:5060
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "xlog.so"
loadmodule "ctl.so"
loadmodule "mi_rpc.so"
loadmodule "debugger.so"
loadmodule "app_python.so"
# ----------------- setting module-specific parameters ---------------
# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
####### Routing Logic ########
modparam("app_python", "load", "/etc/kamailio/kamailio.py")
cfgengine "python"
The file /etc/kamailio/kamailio.py
with the routing logic for runtime:
import sys
import KSR as KSR
def dumpObj(obj):
for attr in dir(obj):
KSR.info("obj.%s = %s\n" % (attr, getattr(obj, attr)))
def mod_init():
KSR.info("===== from Python mod init\n")
# dumpObj(KSR)
return kamailio()
class kamailio:
def __init__(self):
KSR.info('===== kamailio.__init__\n')
def child_init(self, rank):
KSR.info('===== kamailio.child_init(%d)\n' % rank)
return 0
def ksr_request_route(self, msg):
KSR.info("===== request - from kamailio python script\n")
KSR.setdsturi("sip:alice@127.0.0.1:5080")
KSR.tm.t_on_branch("ksr_branch_route_one")
KSR.tm.t_on_reply("ksr_onreply_route_one")
KSR.tm.t_on_failure("ksr_failure_route_one")
KSR.sl.send_reply(100, "Trying")
if KSR.tm.t_relay() < 0 :
KSR.sl.send_reply(500, "Server error")
return 1
def ksr_reply_route(self, msg):
KSR.info("===== response - from kamailio python script\n")
return 1
def ksr_onsend_route(self, msg):
KSR.info("===== onsend route - from kamailio python script\n")
return 1
def ksr_branch_route_one(self, msg):
KSR.info("===== branch route - from kamailio python script\n")
return 1
def ksr_onreply_route_one(self, msg):
KSR.info("===== onreply route - from kamailio python script\n")
return 1
def ksr_failure_route_one(self, msg):
KSR.info("===== failure route - from kamailio python script\n")
return 1
Ruby KEMI InterpreterLink
It is implemented by app_ruby
module. The module requires libruby-dev in order
to be compiled. The module was initially tested with libruby-2.3 and libruby-2.5.
loadmodule "app_ruby.so"
modparam("app_ruby", "load", "/path/to/script.rb")
cfgengine "ruby"
The documentation for app_ruby
is available at:
Squirrel KEMI InterpreterLink
It is implemented by app_sqlang
module. The Squirrel language interpreter is imported inside the module from
Squirrel project, therefore it doesn't require to install any external libraries.
loadmodule "app_sqlang.so"
modparam("app_sqlang", "load", "/path/to/script.sq")
cfgengine "sqlang"
A complete example of using Squirrel as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGSQLANG
The file kamailio-basic-kemi-sqlang.sq
has to be saved to local disk and the load
parameter for app_sqlang
module inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_sqlang
is available at:
KEMI FunctionsLink
Inside the routing script, the functions exported by Kamailio through KEMI are available via KSR
module. With very
few exceptions, the KEMI functions return either an integer or a bool value and can take up to six parameters of type
string or integer.
The integer return code from a Kemi functions has to be evaluated with the following rules:
the value is greater than 0
, then the function was successfully executed and a logical evaluation should be considered truethe value is less than 0
, then the function was not successfully executed or the function was successfully executed and the a logical evaluation should be considered falsethe value is equal to 0
, then the execution of the KEMI script should be terminated (be careful with execution ofexit()
function from the embedded interpreter language, it may kill the interpreter completely, which in this case is Kamailio, resulting in shutting down Kamailio - hint: checkKSR.x.exit()
)
The bool return code is expected to be evaluated as true
or false
inside the KEMI script.
If a function has void
as return type in the signature, the it doesn't return any value.
Several functions may return a string or xval
value, for example in the KSR.pv
submodule to get the value of pseudo-variables. If a function returns xval
, then the result value can be string
, integer
or null
.
The convention for the parameters in the signature of the functions is to enclose in double quotes if the parameter has a string type and no quotes if the parameter has integer type.
Most of the functions exported through KEMI have an equivalent in the functions available in the native scripting language. Generic mapping rules:
- if a parameter value is expected to be used as integer, then KEMI function has it as integer parameter (note: in the native scripting language, it used to be a rule that all parameters of the functions exported by modules to be provided as string)
- if a function from native scripting language has variants with different number of parameters, then KEMI exports one function for each of the variants (to cope with the scripting languages that do not support variadic number of parameters). The names of the related functions are similar.
The available KEMI functions in a running instance of Kamailio can be listed via an RPC command. A matter of the interpreter used, one of the following commands needs to be run:
kamctl rpc app_jsdt.api_list
kamctl rpc app_lua.api_list
kamctl rpc app_python.api_list
kamctl rpc app_python3.api_list
kamctl rpc app_ruby.api_list
kamctl rpc app_sqlang.api_list
Functions Returning 0Link
The functions exported by Kamailio via KSR submodules that return 0 were designed to stop the execution of the routing script. That is done automatically in the native scripting language, but in KEMI scripting languages requires additional steps to terminate the script execution for that SIP message -- typically means evaluation of the return code and if it is 0, then execute KSR.x.exit() or the equivalent in that scripting language.
There are only a few functions returning 0, this section tries to collect them for convenience:
tm
modulet_check_trans()
t_newtran()
- websocket module
ws_handshake()
Example handling t_check_trans()
for 0 return code in Lua script:
...
-- SIP request routing
-- equivalent of request_route{}
function ksr_request_route()
...
if KSR.tm.t_check_trans()==0 then
KSR.x.exit();
end
...
end
...
Note: these functions can also return negative or positive values, those cases have to be handled as well.
Exporting C Function To KEMI InterpretersLink
Because Kamailio needs to load modules in order to export useful functions to KEMI, statical wrappers to C functions implemented in other modules cannot be used, because they will introduce dependencies on each embedded interpreter for all modules.
The implementation relies on defining a set of generic functions that are exported to each embedded interpreter, which are associated at startup with a Kamailio C functions. The lookup at runtime is by an integer index, therefore very fast.
Currently the association table size is 1024 (it means that there can be maximum 1024 Kamailio C functions exported
to the interpreter by a configuration file). The number can be increased, but it should be fairly enough as all
kamailio.cfg
functions are around 1000 and it is no real use case to load all the modules at the same time for use
in production. Also, many functions may not be exported to an embedded language, as they have native alternative in
the embedded language.
Each existing component of Kamailio (e.g., module), can export new functions to KEMI in the following way:
- declare an array of type sr_kemi_t
- register it to KEMI in mod_register() function (or at startup for core components) using sr_kemi_modules_add()
The structure sr_kemi_t
is declared in Kamailio core, the file kemi.h
:
#define SR_KEMI_PARAMS_MAX 6
typedef struct sr_kemi {
str mname; /* sub-module name */
str fname; /* function name */
int rtype; /* return type (supported SR_KEMIP_INT, SR_KEMIP_BOOL, SR_KEMIP_XVAL) */
void *func; /* pointer to the C function to be executed */
int ptypes[SR_KEMI_PARAMS_MAX]; /* array with the type of parameters */
} sr_kemi_t;
Next C code snippet shows how sl module exports two functions:
- C function
sl_send_reply_str(…)
is exported assl.sreply(…)
- C function
send_reply(…)
is exported assl.freply(…)
static sr_kemi_t sl_kemi_exports[] = {
{ str_init("sl"), str_init("sreply"),
SR_KEMIP_INT, sl_send_reply_str,
{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
},
{ str_init("sl"), str_init("freply"),
SR_KEMIP_INT, send_reply,
{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
},
{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
};
int mod_register(char *path, int *dlflags, void *p1, void *p2)
{
sr_kemi_modules_add(sl_kemi_exports);
return 0;
}
Note that the exported array is ended by a sentinel of 0
/NULL
values for all fields.
Exported functions must take first parameter as sip_msg_t*
type (which is the structure with the SIP message being
processed), then followed by up to 6 int
or str*
parameters. When SR_KEMIP_NONE
is given in the array with the types
of parameters, it means there is no parameter from there on (some compilers may rise warning, so it is recommended
to fill all 6 items in array).
The functions exported by Kamailio core are listed inside the array _sr_kemi_core
from the file kemi.c
.
Not all combinations of extra (after sip_msg_t*
) parameters types are supported right now - currently the are:
1 param
- can beint
ofstr*
2 params
- any combination ofint
orstr*
3 params
- any combination ofint
orstr*
4 params
- any combination ofint
orstr*
5 params
- any combination ofint
orstr*
6 params
- all have to bestr*
(other combinations to be added as needed)
The return type can be:
SR_KEMIP_INT
- returned value is integer and has to be evaluated with the following rules:- if less than 0 - it was an error of processing or equivalent of false in the native scripting language;
- if greater than 0 - it was a successful processing or equivalent of true in the native scripting language;
- if equal to 0 - stop execution of the routing script (done by a few functions
such as
KSR.tm.t_check_trans(...)
) - exceptions to the above rules are the getter functions that return the
integer value of some attribute (e.g.,
KSR.kx.get_status()
,KSR.kx.get_timestamp()
)
SR_KEMIP_BOOL
- returned value can be evaluated in the routing script as TRUE or FALSE. In the C code it has to return 1 for TRUE or 0 for FALSE.SR_KEMIP_XVAL
- returned value depends on the context, can be either integer or string value. For example it is used for the functions that return the value of pseudo-variables KSR.pv.get(...). This return type is supported only for exported functions that have up to 2 parameters.
KEMI And Pseudo-VariablesLink
Kamailio-specific pseudo-variables can be managed using functions exposed by
KSR.pv
module. The KSR.pv
is exported by Kamailio core or by KEMI interpreter
module and should be available in the KEMI scripting language automatically, no
additional Kamailio module needs to be loaded. Of course, availability of
specific variables is still a matter of loading the module implementing them
(e.g., availability of $T(..)
requires loading tmx
module).
Because the pseduo-variables were designed for Kamailio native configuration file scripting language, there are a few constraints and limitations as well as recommendations to be aware of, listed in the next sections.
Constraint Of PV Static NamesLink
In many situations the pseudo-variables expect static names, which was ensured
by parsing kamailio.cfg
at startup. However that cannot be ensured by using
KEMI scripts, Kamailio having no control to the implementation of the scripting
language interpreter, the effect can be that it is possible to define a lot of
pseudo-variables, which can lead to filling the private memory of Kamailio.
For example, using htable
module in native kamailio.cfg
, one defines the
hash table test
via modparam
and in the script uses variables such as
$sht(test=>x)
to access the value of the item with the key x
, or uses
$sht(test=>$rU)
to access the value of the item with the key being the R-URI
username of currently processed SIP request. In such case, two pseudo-variable
structures are defined by Kamailio at startup, corresponding to $sht(test=>x)
and $sht(test=>$rU)
.
When using KEMI scripting, accessing $sht(test=>x)
with KSR.pv.get("$sht(test=>x)")
and $sht(test=>$rU)
with KSR.pv.get("$sht(test=>$rU)")
is ok, because still
two pseudo-variable identifiers are used.
However, someone may try to access the $sht(test=>$rU)
via
KSR.pv.get("$sht(test=>" .. KSR.kx.get_ruser() .. ")")
(example in Lua), then
it leads of defining internally to Kamailio a lot of variables, corresponding to
each different R-URI username processed. Say, there were three requests with
R-URI usernames alice
, bob
and carol
, Kamailio has defined internally
three pseudo-variable structures corresponding to $sht(test=>alice)
, $sht(test=>bob)
and $sht(test=>carol)
.
There are couple of mechanisms trying to cope with this situation, especially for
hash table, including availability of dedicated functions to access hash table
items, or trying to clean up some of the pseudo-variable structures. But they
are not available for all the modules that exposes same risks, such as
sqlops
, ndb_redis
or ndb_mongodb
. Therefore try to avoid using
pseudo-variables with dynamic name.
For example, in the next snippet a single pseudo-variable structure is used
for accessing three hash table items, by leveraging $var(x)
inside $sht(...)
name:
KSR.pvx.var_sets("x", "alice");
shtx = KSR.pv_get("$sht(test=>$var(x))");
KSR.pvx.var_sets("x", "bob");
shtx = KSR.pv_get("$sht(test=>$var(x))");
KSR.pvx.var_sets("x", "carol");
shtx = KSR.pv_get("$sht(test=>$var(x))");
Note that for the modules that return results of SQL or noSQL queries, the result id can be reused. The values of result containers are stored in private memory and it is safe to use them in different Kamailio worker processes.
Functions For Specific Pseudo-VariablesLink
The package KSR.kx
exported by kemix
module offers a consistent set of
functions to get or set values specific for various pseudo-variables, especially
for attributes of the SIP message such as From-URI, From-URI-username, etc.
The package KSR.pvx
exported by pv
module offers convenience functions
to work with private-memory ($var(...)
) or shared memory ($shv(...)
)
pseudo-variables as well as XAVPs variants.
For example:
KSR.pv.sets("$var(x)", "alice");
is the same as:
KSR.pvx.var_sets("x", "alice");
the second variant being recommended to be used.
The package KSR.sqlops
exported by sqlops
module offers convenience functions
to access the SQL result instead of using $dbr(...)
.
For example:
KSR.sqlops.sql_query("ca", "select username from subscriber limit 1", "ra");
if KSR.pv.get("$dbr(ra=>rows)") > 0 then
local username = KSR.pv.get("$dbr(ra=>[0,0])");
end
is the same as:
KSR.sqlops.sql_query("ca", "select username from subscriber limit 1", "ra");
if KSR.sqlops.sql_num_rows("ra") > 0 then
local username = KSR.sqlops.sql_result_get("ra", 0, 0);
end
the second variant being recommended to be used.
It is recommended to look at the functions exported to KEMI by each Kamailio module used in the script, there can be useful functions that could be convenient to use instead of the pseudo-variables exported by that module.