浏览代码

db_redis: Use schema files and improve keys def

* Auto-generate schema files for redis from xml specs and use
  them in module instead of having to define them as mod params.
* Allow key definition line by line with multiple "keys" mod params.
* Fetch table versions from schema to avoid having to populate them
  in Redis.
* Fix reconnection issues on connection drops when Redis takes longer
  to start.
* Fix documentation formatting issues.
Andreas Granig 7 年之前
父节点
当前提交
56ad14236c
共有 81 个文件被更改,包括 864 次插入358 次删除
  1. 126 0
      doc/stylesheets/dbschema_k/xsl/db_redis.xsl
  2. 17 2
      src/lib/srdb1/schema/Makefile
  3. 76 86
      src/modules/db_redis/README
  4. 14 4
      src/modules/db_redis/db_redis_mod.c
  5. 1 1
      src/modules/db_redis/db_redis_mod.h
  6. 0 4
      src/modules/db_redis/doc/db_redis.xml
  7. 142 140
      src/modules/db_redis/doc/db_redis_admin.xml
  8. 19 11
      src/modules/db_redis/redis_connection.c
  9. 82 12
      src/modules/db_redis/redis_dbase.c
  10. 244 98
      src/modules/db_redis/redis_table.c
  11. 3 0
      src/modules/db_redis/redis_table.h
  12. 2 0
      utils/kamctl/db_redis/kamailio/acc
  13. 2 0
      utils/kamctl/db_redis/kamailio/acc_cdrs
  14. 2 0
      utils/kamctl/db_redis/kamailio/active_watchers
  15. 2 0
      utils/kamctl/db_redis/kamailio/address
  16. 2 0
      utils/kamctl/db_redis/kamailio/aliases
  17. 2 0
      utils/kamctl/db_redis/kamailio/carrier_name
  18. 2 0
      utils/kamctl/db_redis/kamailio/carrierfailureroute
  19. 2 0
      utils/kamctl/db_redis/kamailio/carrierroute
  20. 2 0
      utils/kamctl/db_redis/kamailio/cpl
  21. 2 0
      utils/kamctl/db_redis/kamailio/dbaliases
  22. 2 0
      utils/kamctl/db_redis/kamailio/dialog
  23. 2 0
      utils/kamctl/db_redis/kamailio/dialog_vars
  24. 2 0
      utils/kamctl/db_redis/kamailio/dialplan
  25. 2 0
      utils/kamctl/db_redis/kamailio/dispatcher
  26. 2 0
      utils/kamctl/db_redis/kamailio/domain
  27. 2 0
      utils/kamctl/db_redis/kamailio/domain_attrs
  28. 2 0
      utils/kamctl/db_redis/kamailio/domain_name
  29. 2 0
      utils/kamctl/db_redis/kamailio/domainpolicy
  30. 2 0
      utils/kamctl/db_redis/kamailio/dr_gateways
  31. 2 0
      utils/kamctl/db_redis/kamailio/dr_groups
  32. 2 0
      utils/kamctl/db_redis/kamailio/dr_gw_lists
  33. 2 0
      utils/kamctl/db_redis/kamailio/dr_rules
  34. 2 0
      utils/kamctl/db_redis/kamailio/globalblacklist
  35. 2 0
      utils/kamctl/db_redis/kamailio/grp
  36. 2 0
      utils/kamctl/db_redis/kamailio/htable
  37. 2 0
      utils/kamctl/db_redis/kamailio/imc_members
  38. 2 0
      utils/kamctl/db_redis/kamailio/imc_rooms
  39. 2 0
      utils/kamctl/db_redis/kamailio/lcr_gw
  40. 2 0
      utils/kamctl/db_redis/kamailio/lcr_rule
  41. 2 0
      utils/kamctl/db_redis/kamailio/lcr_rule_target
  42. 2 0
      utils/kamctl/db_redis/kamailio/location
  43. 2 0
      utils/kamctl/db_redis/kamailio/location_attrs
  44. 2 0
      utils/kamctl/db_redis/kamailio/matrix
  45. 2 0
      utils/kamctl/db_redis/kamailio/missed_calls
  46. 2 0
      utils/kamctl/db_redis/kamailio/mohqcalls
  47. 2 0
      utils/kamctl/db_redis/kamailio/mohqueues
  48. 2 0
      utils/kamctl/db_redis/kamailio/mtree
  49. 2 0
      utils/kamctl/db_redis/kamailio/mtrees
  50. 2 0
      utils/kamctl/db_redis/kamailio/pdt
  51. 2 0
      utils/kamctl/db_redis/kamailio/pl_pipes
  52. 2 0
      utils/kamctl/db_redis/kamailio/presentity
  53. 2 0
      utils/kamctl/db_redis/kamailio/pua
  54. 2 0
      utils/kamctl/db_redis/kamailio/purplemap
  55. 2 0
      utils/kamctl/db_redis/kamailio/re_grp
  56. 2 0
      utils/kamctl/db_redis/kamailio/rls_presentity
  57. 2 0
      utils/kamctl/db_redis/kamailio/rls_watchers
  58. 2 0
      utils/kamctl/db_redis/kamailio/rtpengine
  59. 2 0
      utils/kamctl/db_redis/kamailio/rtpproxy
  60. 2 0
      utils/kamctl/db_redis/kamailio/sca_subscriptions
  61. 2 0
      utils/kamctl/db_redis/kamailio/silo
  62. 2 0
      utils/kamctl/db_redis/kamailio/sip_trace
  63. 2 0
      utils/kamctl/db_redis/kamailio/speed_dial
  64. 2 0
      utils/kamctl/db_redis/kamailio/subscriber
  65. 2 0
      utils/kamctl/db_redis/kamailio/topos_d
  66. 2 0
      utils/kamctl/db_redis/kamailio/topos_t
  67. 2 0
      utils/kamctl/db_redis/kamailio/trusted
  68. 2 0
      utils/kamctl/db_redis/kamailio/uacreg
  69. 2 0
      utils/kamctl/db_redis/kamailio/uid_credentials
  70. 2 0
      utils/kamctl/db_redis/kamailio/uid_domain
  71. 2 0
      utils/kamctl/db_redis/kamailio/uid_domain_attrs
  72. 2 0
      utils/kamctl/db_redis/kamailio/uid_global_attrs
  73. 2 0
      utils/kamctl/db_redis/kamailio/uid_uri
  74. 2 0
      utils/kamctl/db_redis/kamailio/uid_uri_attrs
  75. 2 0
      utils/kamctl/db_redis/kamailio/uid_user_attrs
  76. 2 0
      utils/kamctl/db_redis/kamailio/uri
  77. 2 0
      utils/kamctl/db_redis/kamailio/userblacklist
  78. 2 0
      utils/kamctl/db_redis/kamailio/usr_preferences
  79. 2 0
      utils/kamctl/db_redis/kamailio/version
  80. 2 0
      utils/kamctl/db_redis/kamailio/watchers
  81. 2 0
      utils/kamctl/db_redis/kamailio/xcap

+ 126 - 0
doc/stylesheets/dbschema_k/xsl/db_redis.xsl

@@ -0,0 +1,126 @@
+<?xml version='1.0'?>
+<!--
+ * $Id: $
+ *
+ * XSL converter script for db_redis
+ *
+ * Copyright (C) 2001-2007 FhG Fokus
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+-->
+
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                version='1.0'
+                xmlns:xi="http://www.w3.org/2001/XInclude">
+
+    <xsl:import href="common.xsl"/>
+    <xsl:output method="text" indent="no" omit-xml-declaration="yes"/>
+
+    <!-- Create the file for the table in dbtext subdirectory -->
+    <xsl:template match="table">
+	<xsl:variable name="name">
+	    <xsl:call-template name="get-name"/>
+	</xsl:variable>
+
+	<xsl:variable name="path" select="concat($dir, concat('/', concat($prefix, $name)))"/>
+	<xsl:document href="{$path}" method="text" indent="no" omit-xml-declaration="yes">
+		<xsl:apply-imports/>
+		<!-- Insert version data -->
+		 <xsl:apply-templates select="version"/>
+		<!-- this is not exactly what we want for dbtext, as the version data gets
+		     appended to the actual table file, and no to the 'version' table.
+		     But its not possible (at least with XSL 1.0, AFAIK) to append data to a
+		     file. So it's much more easier to do this in the Makefile -->
+	</xsl:document>
+    </xsl:template>
+
+    <!-- version data template -->
+    <xsl:template match="version">
+	<xsl:value-of select="text()"/>
+	<xsl:text>&#x0A;</xsl:text>
+    </xsl:template>
+
+    <!-- Create column definitions -->
+    <xsl:template match="column">
+	<xsl:variable name="type">
+	    <xsl:call-template name="get-type"/>
+	</xsl:variable>
+
+	<xsl:variable name="null">
+	    <xsl:call-template name="get-null"/>
+	</xsl:variable>
+
+	<xsl:call-template name="get-name"/>
+	<xsl:text>/</xsl:text>
+	<xsl:choose>
+	    <xsl:when test="type[@db=$db]">
+		<xsl:value-of select="normalize-space(type[@db=$db])"/>
+	    </xsl:when>
+	    <xsl:when test="$type='char' or
+						$type='short' or
+						$type='int' or
+						$type='long' or
+						$type='datetime'">
+		<xsl:text>int</xsl:text>
+	    </xsl:when>
+	    <xsl:when test="$type='float' or
+						$type='double'">
+		<xsl:text>double</xsl:text>
+	    </xsl:when>
+	    <xsl:when test="$type='string' or
+						$type='text' or
+						$type='binary' or
+                        $type='largetext' or
+						$type='largebinary'">
+		<xsl:text>string</xsl:text>
+	    </xsl:when>
+	    <xsl:otherwise>
+		<xsl:call-template name="type-error"/>
+	    </xsl:otherwise>
+	</xsl:choose>
+
+    <!--
+	<xsl:if test="$null=1">
+	    <xsl:text>&amp;null</xsl:text>
+    </xsl:if>
+    -->
+	<xsl:text>,</xsl:text>
+	<xsl:if test="position()=last()">
+	    <xsl:text>&#x0A;</xsl:text>
+	</xsl:if>
+    </xsl:template>
+
+    <!-- Escape all : occurrences -->
+    <xsl:template name="escape">
+	<xsl:param name="value"/>
+	<xsl:choose>
+	    <xsl:when test="contains($value, ':')">
+		<xsl:value-of select="concat(substring-before($value, ':'), '\:')"/>
+		<xsl:call-template name="escape">
+		    <xsl:with-param name="value" select="substring-after($value, ':')"/>
+		</xsl:call-template>
+	    </xsl:when>
+	    <xsl:otherwise>
+		<xsl:value-of select="$value"/>
+	    </xsl:otherwise>
+	</xsl:choose>
+    </xsl:template>
+
+</xsl:stylesheet>

+ 17 - 2
src/lib/srdb1/schema/Makefile

@@ -37,6 +37,9 @@ ORACLE_XSL = $(STYLESHEETS)/oracle.xsl
 # Stylesheet used to generate mongodb database schema
 MONGODB_XSL = $(STYLESHEETS)/mongodb.xsl
 
+# Stylesheet used to generate Redis database schema
+DB_REDIS_XSL = $(STYLESHEETS)/db_redis.xsl
+
 # Stylesheet used to generate docbook documentation
 DOCBOOK_XSL = $(STYLESHEETS)/docbook.xsl
 
@@ -69,7 +72,7 @@ ifeq ($(VERBOSE), 1)
 	override XSLTPROC := $(XSLTPROC) --verbose
 endif
 
-all: mysql postgres dbtext db_berkeley db_sqlite oracle mongodb pi_framework
+all: mysql postgres dbtext db_berkeley db_sqlite oracle mongodb db_redis pi_framework
 
 .PHONY: pi_framework pi_framework_clean
 pi_framework:
@@ -227,6 +230,18 @@ mongodb:
 mongodb_clean:
 	-@rm -f $(SCHEME)/mongodb/kamailio/*
 
+.PHONY: db_redis db_redis_clean
+db_redis:
+	for FILE in $(TABLES); do \
+		XML_CATALOG_FILES=$(CATALOG) $(XSLTPROC) $(XSLTPROC_FLAGS) \
+		--stringparam dir "$(SCHEME)/db_redis/kamailio" \
+		--stringparam prefix "" \
+		--stringparam db "db_redis" \
+		$(DB_REDIS_XSL) kamailio-"$$FILE".xml ; \
+	done
+
+db_redis_clean:
+	-@rm -f $(SCHEME)/db_redis/*
 
 .PHONY: docbook-xml
 docbook-xml:
@@ -350,4 +365,4 @@ dbdoc_clean:
 	done
 
 .PHONY: clean
-clean: mysql_clean postgres_clean oracle_clean dbtext_clean db_berkeley_clean db_sqlite_clean pi_framework_clean docbook_clean # modules_clean dbdoc_clean
+clean: mysql_clean postgres_clean oracle_clean dbtext_clean db_berkeley_clean db_sqlite_clean mongodb_clean db_redis_clean pi_framework_clean docbook_clean # modules_clean dbdoc_clean

+ 76 - 86
src/modules/db_redis/README

@@ -18,24 +18,22 @@ Andreas Granig
    1. Admin Guide
 
         1. Overview
+        2. Limitations
+        3. Dependencies
 
-              1.1. Limitations
+              3.1. Kamailio Modules
 
-        2. Dependencies
+        4. Parameters
 
-              2.1. Kamailio Modules
-              2.2. Parameters
+              4.1. schema_path (string)
+              4.2. keys (string)
 
-                    2.2.1. schema (string)
-                    2.2.2. keys (string)
-
-              2.3. External Libraries or Applications
-
-        3. Usage
+        5. External Libraries or Applications
+        6. Usage
 
    List of Examples
 
-   1.1. Setting schema module parameter
+   1.1. Setting schema_path module parameter
    1.2. Setting keys module parameter
    1.3. Usage
 
@@ -44,25 +42,21 @@ Chapter 1. Admin Guide
    Table of Contents
 
    1. Overview
+   2. Limitations
+   3. Dependencies
 
-        1.1. Limitations
-
-   2. Dependencies
+        3.1. Kamailio Modules
 
-        2.1. Kamailio Modules
-        2.2. Parameters
+   4. Parameters
 
-              2.2.1. schema (string)
-              2.2.2. keys (string)
+        4.1. schema_path (string)
+        4.2. keys (string)
 
-        2.3. External Libraries or Applications
-
-   3. Usage
+   5. External Libraries or Applications
+   6. Usage
 
 1. Overview
 
-   1.1. Limitations
-
    This module provides a DB APIv1 connector for Redis server.
 
    It can be used as a replacement for other database modules such as
@@ -71,17 +65,20 @@ Chapter 1. Admin Guide
    specific modules. Also, for proper performance, the module needs
    particular configuration tailored to the using modules.
 
-   Since Redis does not provide a schema, a schema has to be defined as
-   module parameter "schema". The schema definition is composed of a
-   semi-column separated list of table definitions in format
-   <table-name>=<column-name>/<type>[<column-name>/<type> ...].
-
-   Example:
-        version=table_name/string,table_version/int;location=username/string,dom
-ain/string,contact/string,received/string,path/string,expires/timestamp,q/double
-,callid/string,cseq/int,last_modified/timestamp,flags/int,cflags/int,user_agent/
-string,socket/string,methods/int,ruid/string,reg_id/int,instance/string,server_i
-d/int,connection_id/int,keepalive/int,partition/int
+   Since Redis does not provide a schema by itself, db_redis ships with
+   schema files, which path has to be defined as module parameter
+   "schema_path". The schema definition is defined in one file per table
+   with the file name being the table name, and each file is composed of a
+   comma-separated list of column definitions in format
+   <column-name>/<type>[,<column-name>/<type> ...] in one line, followed
+   by a line holding the table version.
+
+   Example for location definition:
+username/string,domain/string,contact/string,received/string,path/string,expires
+/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/int,cfl
+ags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,insta
+nce/string,server_id/int,connection_id/int,keepalive/int,partition/int
+8
 
    Also since Redis is a key-value store with keys having to be unique,
    tables and rows e.g. from MySQL can not be ported 1:1 to Redis. For
@@ -98,85 +95,76 @@ d/int,connection_id/int,keepalive/int,partition/int
    keys/values the query is constructed by usrloc to look for the final
    entry key in the mapping set first, then querying the actual entries
    from there, avoiding full table scans. For usrloc, the same holds true
-   for exipired contacts, requiring a different kind of mapping. There is
-   a certain balance of read performance vs. write performance to
-   consider, because inserts and deletes also have to maintain the
-   mappings, in favor of much faster selects. The mappings can be freely
-   defined, so even though other kamailio modules don't require a specific
-   mapping to be in place for proper performance, mappings could be
-   defined for external applications to read faster (for instance letting
-   the acc module also write mappings besides the actual records for
-   billing systems to correlate start and stop records faster).
+   for expired contacts, requiring a different kind of mapping. There is a
+   certain balance of read performance vs. write performance to consider,
+   because inserts and deletes also have to maintain the mappings, in
+   favor of much faster selects. The mappings can be freely defined, so
+   even though other kamailio modules don't require a specific mapping to
+   be in place for proper performance, mappings could be defined for
+   external applications to read faster (for instance letting the acc
+   module also write mappings besides the actual records for billing
+   systems to correlate start and stop records faster).
 
    The mappings can be freely defined in the "keys" module parameter. It
    is composed of a semi-colon separated list of definitions in format
    <table-name>=<entry>:<column-name>[&<map-name>:<column-name>,<column-na
-   me>...]
+   me>...]. Each table must at least have an "entry" key for db_redis to
+   be able to store data.
 
    Example:
-        version=entry:table_name;location=entry:ruid&usrdom:username,domain&time
-r:partition,keepalive;acc=entry:callid,time_hires&cid:callid
+location=entry:ruid&usrdom:username,domain&timer:partition,keepalive;acc=entry:c
+allid,time_hires&cid:callid
 
-   Note that as of now, you have to have version information in your Redis
-   db, similar to your MySQL schema. To insert table versions (e.g. for
-   usrloc and acc), execute the following:
-        # redis-cli -h $host -n $dbnumber HMSET version:entry::location table_ve
-rsion 8
-        # redis-cli -h $host -n $dbnumber HMSET version:entry::acc table_version
- 5
+   For readability purposes, keys per table can be defined line by line by
+   providing multiple "keys" mod-params.
 
    You can read more about Redis at: https://www.redis.io.
 
-1.1. Limitations
+2. Limitations
 
      * This module has implemented the equivalent operations for INSERT,
        UPDATE, DELETE and SELECT. The ORDER BY for SELECT is not
        implemented. Raw query is not implemented inside this module, use
-       db_redis for sending any kind of command to a Redis server.
-
-2. Dependencies
-
-   2.1. Kamailio Modules
-   2.2. Parameters
+       ndb_redis for sending any kind of command to a Redis server.
 
-        2.2.1. schema (string)
-        2.2.2. keys (string)
+3. Dependencies
 
-   2.3. External Libraries or Applications
+   3.1. Kamailio Modules
 
-2.1. Kamailio Modules
+3.1. Kamailio Modules
 
    The following modules must be loaded before this module:
      * none.
 
-2.2. Parameters
+4. Parameters
 
-2.2.1. schema (string)
+   4.1. schema_path (string)
+   4.2. keys (string)
 
-   The schema of your tables.
+4.1. schema_path (string)
 
-   Example 1.1. Setting schema module parameter
-modparam("db_redis", "schema", "version=table_name/string,table_version/int;loca
-tion=username/string,domain/string,contact/string,received/string,path/string,ex
-pires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/in
-t,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,
-instance/string,server_id/int,connection_id/int,keepalive/int,partition/int")
+   The path to the schema of your tables (default
+   /usr/share/kamailio/db_redis).
 
-2.2.2. keys (string)
+   Example 1.1. Setting schema_path module parameter
+modparam("db_redis", "schema_path", "/usr/local/share/kamailio/db_redis/kamailio
+")
 
-   The lookup and mapping keys of your tables.
+4.2. keys (string)
+
+   The entry and mapping keys of your tables.
 
    Example 1.2. Setting keys module parameter
 modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&usrdo
 m:username,domain&timer:partition,keepalive")
 
-2.3. External Libraries or Applications
+5. External Libraries or Applications
 
    The following libraries or applications must be installed before
    running Kamailio with this module loaded:
      * hiredis - available at https://github.com/redis/hiredis
 
-3. Usage
+6. Usage
 
    Load the module and set the the DB URL for specific modules to:
    redis://[username]@host:port/database. Username is optional. Database
@@ -186,14 +174,16 @@ m:username,domain&timer:partition,keepalive")
 ...
 loadmodule "db_redis.so"
 ...
-#!define DBURL "redis://127.0.0.1:6379/5"
+#!define DBURL_USRLOC "redis://127.0.0.1:6379/5"
+#!define DBURL_ACC    "redis://127.0.0.1:6379/6"
+#!define DBURL_AUTH   "redis://127.0.0.1:6379/7"
 ...
-modparam("db_redis", "schema", "version=table_name/string,table_version/int;loca
-tion=username/string,domain/string,contact/string,received/string,path/string,ex
-pires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/in
-t,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,
-instance/string,server_id/int,connection_id/int,keepalive/int,partition/int")
-modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&usrdo
-m:username,domain&timer:partition,keepalive")
-modparam("usrloc", "db_url", DBURL)
+modparam("db_redis", "schema_path", "/usr/share/kamailio/db_redis/kamailio")
+modparam("db_redis", "keys", "location=entry:ruid&usrdom:username,domain&timer:p
+artition,keepalive")
+modparam("db_redis", "keys", "acc=entry:callid,time_hires&cid:callid")
+modparam("db_redis", "keys", "subscriber=entry:username,domain")
+modparam("usrloc", "db_url", DBURL_USRLOC)
+modparam("acc_db", "db_url", DBURL_ACC)
+modparam("auth_db", "db_url", DBURL_AUTH)
 ...

+ 14 - 4
src/modules/db_redis/db_redis_mod.c

@@ -27,15 +27,17 @@
 
 #include "db_redis_mod.h"
 #include "redis_dbase.h"
+#include "redis_table.h"
 
 MODULE_VERSION
 
 str redis_keys = str_init("");
-str redis_schema = str_init("");
+str redis_schema_path = str_init("/usr/share/kamailio/db_redis/kamailio");
 
 static int db_redis_bind_api(db_func_t *dbb);
 static int mod_init(void);
 static void mod_destroy(void);
+int keys_param(modparam_t type, void* val);
 
 static cmd_export_t cmds[] = {
     {"db_bind_api",    (cmd_function)db_redis_bind_api,    0, 0, 0, 0},
@@ -47,8 +49,8 @@ static cmd_export_t cmds[] = {
  * Exported parameters
  */
 static param_export_t params[] = {
-    {"keys", PARAM_STR, &redis_keys },
-    {"schema", PARAM_STR, &redis_schema },
+    {"keys",        PARAM_STRING|USE_FUNC_PARAM, (void*)keys_param},
+    {"schema_path", PARAM_STR, &redis_schema_path },
     {0, 0, 0}
 };
 
@@ -89,6 +91,14 @@ static int db_redis_bind_api(db_func_t *dbb) {
     return 0;
 }
 
+int keys_param(modparam_t type, void *val)
+{
+	if (val == NULL)
+		return -1;
+	else
+		return db_redis_keys_spec((char*)val);
+}
+
 int mod_register(char *path, int *dlflags, void *p1, void *p2) {
     if(db_api_init()<0)
         return -1;
@@ -102,4 +112,4 @@ static int mod_init(void) {
 
 static void mod_destroy(void) {
     LM_DBG("module destroying\n");
-}
+}

+ 1 - 1
src/modules/db_redis/db_redis_mod.h

@@ -48,6 +48,6 @@
 #define REDIS_HT_SIZE 8
 
 extern str redis_keys;
-extern str redis_schema;
+extern str redis_schema_path;
 
 #endif /* _DB_REDIS_MOD_H */

+ 0 - 4
src/modules/db_redis/doc/db_redis.xml

@@ -7,7 +7,6 @@
 %docentities;
 
 ]>
-
 <book xmlns:xi="http://www.w3.org/2001/XInclude">
     <bookinfo>
 	<title>DB_REDIS Module</title>
@@ -30,8 +29,5 @@
 	</copyright>
     </bookinfo>
     <toc></toc>
-    
     <xi:include href="db_redis_admin.xml"/>
-    
-    
 </book>

+ 142 - 140
src/modules/db_redis/doc/db_redis_admin.xml

@@ -10,181 +10,183 @@
 <!-- Module User's Guide -->
 
 <chapter>
-	
 	<title>&adminguide;</title>
-	
 	<section>
-	<title>Overview</title>
-	<para>
-		This module provides a DB APIv1 connector for Redis server.
-	</para>
-	<para>
-	It can be used as a replacement for other database modules such as
-	db_mysql and db_postgres. Not all the specs of DB APIv1 are
-	implemented, thus the usage of this module might be restricted to
-	specific modules. Also, for proper performance, the module needs
-	particular configuration tailored to the using modules.
-	</para>
-	<para>
-	Since Redis does not provide a schema, a schema has to be defined as
-	module parameter "schema". The schema definition is composed of a
-	semi-column	separated list of table definitions in format
-	&lt;table-name&gt;=&lt;column-name&gt;/&lt;type&gt;[,&lt;column-name&gt;/&lt;type&gt; ...].
-	</para>
-	<para>
-	Example:
-	<programlisting format="linespecific">
-	version=table_name/string,table_version/int;location=username/string,domain/string,contact/string,received/string,path/string,expires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/int,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,instance/string,server_id/int,connection_id/int,keepalive/int,partition/int
-	</programlisting>
-	</para>
-	<para>
-	Also since Redis is a key-value store with keys having to be unique,
-	tables and rows e.g. from MySQL can not be ported 1:1 to Redis. For
-	instance, usrloc relies on a key "username@domain", but it must not be
-	unique for being able to store multiple contacts per AoR. Thus, db_redis
-	supports mapping sets in a way for example for usrloc to have a set with
-	a key "username@domain", with its entries being unique keys per contact being
-	the ruid of a contact. Thus, one contact in usrloc consists of a unique
-	key "location:entry::example-ruid-1" being a hash with the columns like
-	username, domain, contact, path etc. In addition, this unique key is stored
-	in a set "location:usrdom::exampleuser:exampledomain.org". When usrloc does
-	a lookup based on "username@domain", db_redis figures out via the keys/values
-	the query is constructed by usrloc to look for the final entry key in the
-	mapping set first, then querying the actual entries from there, avoiding full
-	table scans. For usrloc, the same holds true for expired contacts, requiring
-	a different kind of mapping. There is a certain balance of read performance
-	vs. write performance to consider, because inserts and deletes also have to
-	maintain the mappings, in favor of much faster selects. The mappings can be
-	freely defined, so even though other kamailio modules don't require a specific
-	mapping to be in place for proper performance, mappings could be defined
-	for external applications to read faster (for instance letting the acc module
-	also write mappings besides the actual records for billing systems to
-	correlate start and stop records faster).
-	</para>
-	<para>
-	The mappings can be freely defined in the "keys" module parameter. It is
-	composed of a semi-colon separated list of definitions in format
-	&lt;table-name&gt;=&lt;entry&gt;:&lt;column-name&gt;[&amp;&lt;map-name&gt;:&lt;column-name&gt;,&lt;column-name&gt;...].
-	Each table must at least have an "entry" key for db_redis to be able to store data.
-	</para>
-	<para>
-	Example:
-	<programlisting format="linespecific">
-	version=entry:table_name;location=entry:ruid&amp;usrdom:username,domain&amp;timer:partition,keepalive;acc=entry:callid,time_hires&amp;cid:callid
-	</programlisting>
-	</para>
-	<para>
-	Note that as of now, you have to have version information in your Redis db,
-	similar to your MySQL schema. To insert table versions (e.g. for usrloc and acc),
-	execute the following:
-	</para>
-	<programlisting format="linespecific">
-	# redis-cli -h $host -n $dbnumber HMSET version:entry::location table_version 8
-	# redis-cli -h $host -n $dbnumber HMSET version:entry::acc table_version 5
-	</programlisting>
-
-	<para>
-		You can read more about Redis at:
-		<ulink url="https://www.redis.io">https://www.redis.io</ulink>.
-	</para>
-
-	<section>
-	<title>Limitations</title>
-	<itemizedlist>
-	<listitem>
-	<para>
-		This module has implemented the equivalent operations for INSERT,
-		UPDATE, DELETE and SELECT. The ORDER BY for SELECT is not implemented.
-		Raw query is not implemented inside this module, use ndb_redis for sending any
-		kind of command to a Redis server.
-	</para>
-	</listitem>
-	</itemizedlist>
-	</section>
+		<title>Overview</title>
+		<para>
+			This module provides a DB APIv1 connector for Redis server.
+		</para>
+		<para>
+			It can be used as a replacement for other database modules such as
+			db_mysql and db_postgres. Not all the specs of DB APIv1 are
+			implemented, thus the usage of this module might be restricted to
+			specific modules. Also, for proper performance, the module needs
+			particular configuration tailored to the using modules.
+		</para>
+		<para>
+			Since Redis does not provide a schema by itself, db_redis ships with
+			schema files, which path has to be defined as module parameter "schema_path".
+			The schema definition is defined in one file per table with the file name
+			being the table name, and each file is composed of a
+			comma-separated list of column definitions in format
+			&lt;column-name&gt;/&lt;type&gt;[,&lt;column-name&gt;/&lt;type&gt; ...]
+			in one line, followed by a line holding the table version.
+		</para>
+		<para>
+			Example for location definition:
+			<programlisting format="linespecific">
+username/string,domain/string,contact/string,received/string,path/string,expires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/int,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,instance/string,server_id/int,connection_id/int,keepalive/int,partition/int
+8
+			</programlisting>
+		</para>
+		<para>
+			Also since Redis is a key-value store with keys having to be unique,
+			tables and rows e.g. from MySQL can not be ported 1:1 to Redis. For
+			instance, usrloc relies on a key "username@domain", but it must not be
+			unique for being able to store multiple contacts per AoR. Thus, db_redis
+			supports mapping sets in a way for example for usrloc to have a set with
+			a key "username@domain", with its entries being unique keys per contact being
+			the ruid of a contact. Thus, one contact in usrloc consists of a unique
+			key "location:entry::example-ruid-1" being a hash with the columns like
+			username, domain, contact, path etc. In addition, this unique key is stored
+			in a set "location:usrdom::exampleuser:exampledomain.org". When usrloc does
+			a lookup based on "username@domain", db_redis figures out via the keys/values
+			the query is constructed by usrloc to look for the final entry key in the
+			mapping set first, then querying the actual entries from there, avoiding full
+			table scans. For usrloc, the same holds true for expired contacts, requiring
+			a different kind of mapping. There is a certain balance of read performance
+			vs. write performance to consider, because inserts and deletes also have to
+			maintain the mappings, in favor of much faster selects. The mappings can be
+			freely defined, so even though other kamailio modules don't require a specific
+			mapping to be in place for proper performance, mappings could be defined
+			for external applications to read faster (for instance letting the acc module
+			also write mappings besides the actual records for billing systems to
+			correlate start and stop records faster).
+		</para>
+		<para>
+			The mappings can be freely defined in the "keys" module parameter. It is
+			composed of a semi-colon separated list of definitions in format
+			&lt;table-name&gt;=&lt;entry&gt;:&lt;column-name&gt;[&amp;&lt;map-name&gt;:&lt;column-name&gt;,&lt;column-name&gt;...].
+			Each table must at least have an "entry" key for db_redis to be able to store data.
+		</para>
+		<para>
+			Example:
+			<programlisting format="linespecific">
+location=entry:ruid&amp;usrdom:username,domain&amp;timer:partition,keepalive;acc=entry:callid,time_hires&amp;cid:callid
+			</programlisting>
+		</para>
+		<para>
+			For readability purposes, keys per table can be defined line by line by
+			providing multiple "keys" mod-params.
+		</para>
+		<para>
+			You can read more about Redis at:
+			<ulink url="https://www.redis.io">https://www.redis.io</ulink>.
+		</para>
 	</section>
 
 	<section>
-	<title>Dependencies</title>
-	<section>
-		<title>&kamailio; Modules</title>
-		<para>
-		The following modules must be loaded before this module:
-			<itemizedlist>
+		<title>Limitations</title>
+		<itemizedlist>
 			<listitem>
-			<para>
-				<emphasis>none</emphasis>.
-			</para>
+				<para>
+					This module has implemented the equivalent operations for INSERT,
+					UPDATE, DELETE and SELECT. The ORDER BY for SELECT is not implemented.
+					Raw query is not implemented inside this module, use ndb_redis for sending any
+					kind of command to a Redis server.
+				</para>
 			</listitem>
-			</itemizedlist>
-		</para>
+		</itemizedlist>
 	</section>
 
 	<section>
-	<title>Parameters</title>
-	<section>
-		<title><varname>schema</varname> (string)</title>
-		<para>
-		The schema of your tables.
-		</para>
-		<example>
-		<title>Setting schema module parameter</title>
-		<programlisting format="linespecific">
-modparam("db_redis", "schema", "version=table_name/string,table_version/int;location=username/string,domain/string,contact/string,received/string,path/string,expires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/int,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,instance/string,server_id/int,connection_id/int,keepalive/int,partition/int")
-		</programlisting>
-		</example>
+		<title>Dependencies</title>
+		<section>
+			<title>&kamailio; Modules</title>
+			<para>
+				The following modules must be loaded before this module:
+				<itemizedlist>
+					<listitem>
+						<para>
+							<emphasis>none</emphasis>.
+						</para>
+					</listitem>
+				</itemizedlist>
+			</para>
+		</section>
 	</section>
+
 	<section>
-		<title><varname>keys</varname> (string)</title>
-		<para>
-		The entry and mapping keys of your tables.
-		</para>
-		<example>
-		<title>Setting keys module parameter</title>
-		<programlisting format="linespecific">
+		<title>Parameters</title>
+		<section>
+			<title><varname>schema_path</varname> (string)</title>
+			<para>
+				The path to the schema of your tables (default /usr/share/kamailio/db_redis).
+			</para>
+			<example>
+				<title>Setting schema_path module parameter</title>
+				<programlisting format="linespecific">
+modparam("db_redis", "schema_path", "/usr/local/share/kamailio/db_redis/kamailio")
+				</programlisting>
+			</example>
+		</section>
+
+		<section>
+			<title><varname>keys</varname> (string)</title>
+			<para>
+				The entry and mapping keys of your tables.
+			</para>
+			<example>
+				<title>Setting keys module parameter</title>
+				<programlisting format="linespecific">
 modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&amp;usrdom:username,domain&amp;timer:partition,keepalive")
-		</programlisting>
-		</example>
-	</section>
+				</programlisting>
+			</example>
+		</section>
 	</section>
 
 	<section>
 		<title>External Libraries or Applications</title>
 		<para>
-		The following libraries or applications must be installed before running
-		&kamailio; with this module loaded:
+			The following libraries or applications must be installed before running
+			&kamailio; with this module loaded:
 			<itemizedlist>
-			<listitem>
-			<para>
-				<emphasis>hiredis</emphasis> - available at
-				<ulink url="https://github.com/redis/hiredis">https://github.com/redis/hiredis</ulink>
-			</para>
-			</listitem>
+				<listitem>
+					<para>
+						<emphasis>hiredis</emphasis> - available at
+						<ulink url="https://github.com/redis/hiredis">https://github.com/redis/hiredis</ulink>
+					</para>
+				</listitem>
 			</itemizedlist>
 		</para>
 	</section>
-	</section>
 
 	<section>
-	<title>Usage</title>
+		<title>Usage</title>
 		<para>
 			Load the module and set the the DB URL for specific modules to:
 			redis://[username]@host:port/database. Username is optional.
 			Database must be a valid redis database number.
 		</para>
 		<example>
-		<title>Usage</title>
-		<programlisting format="linespecific">
+			<title>Usage</title>
+			<programlisting format="linespecific">
 ...
 loadmodule "db_redis.so"
 ...
-#!define DBURL "redis://127.0.0.1:6379/5"
+#!define DBURL_USRLOC "redis://127.0.0.1:6379/5"
+#!define DBURL_ACC    "redis://127.0.0.1:6379/6"
+#!define DBURL_AUTH   "redis://127.0.0.1:6379/7"
 ...
-modparam("db_redis", "schema", "version=table_name/string,table_version/int;location=username/string,domain/string,contact/string,received/string,path/string,expires/timestamp,q/double,callid/string,cseq/int,last_modified/timestamp,flags/int,cflags/int,user_agent/string,socket/string,methods/int,ruid/string,reg_id/int,instance/string,server_id/int,connection_id/int,keepalive/int,partition/int")
-modparam("db_redis", "keys", "version=entry:table_name;location=entry:ruid&amp;usrdom:username,domain&amp;timer:partition,keepalive")
-modparam("usrloc", "db_url", DBURL)
+modparam("db_redis", "schema_path", "/usr/share/kamailio/db_redis/kamailio")
+modparam("db_redis", "keys", "location=entry:ruid&amp;usrdom:username,domain&amp;timer:partition,keepalive")
+modparam("db_redis", "keys", "acc=entry:callid,time_hires&amp;cid:callid")
+modparam("db_redis", "keys", "subscriber=entry:username,domain")
+modparam("usrloc", "db_url", DBURL_USRLOC)
+modparam("acc_db", "db_url", DBURL_ACC)
+modparam("auth_db", "db_url", DBURL_AUTH)
 ...
-</programlisting>
+			</programlisting>
 		</example>
 	</section>
 </chapter>
-

+ 19 - 11
src/modules/db_redis/redis_connection.c

@@ -39,6 +39,7 @@ int db_redis_connect(km_redis_con_t *con) {
     // TODO: on carrier, if we have db fail-over, the currently connected
     // redis server will become slave without dropping connections?
 
+    LM_DBG("connecting to redis at %s:%d\n", con->id->host, con->id->port);
     con->con = redisConnectWithTimeout(con->id->host, con->id->port, tv);
 
     if (!con->con) {
@@ -91,6 +92,7 @@ int db_redis_connect(km_redis_con_t *con) {
         goto err;
     }
     freeReplyObject(reply); reply = NULL;
+    LM_DBG("connection opened to %.*s\n", con->id->url.len, con->id->url.s);
 
     return 0;
 
@@ -125,9 +127,9 @@ km_redis_con_t* db_redis_new_connection(const struct db_id* id) {
     ptr->id = (struct db_id*)id;
 
     /*
-    LM_DBG("trying to initialize connection to '%.*s' with schema '%.*s' and keys '%.*s'\n",
+    LM_DBG("trying to initialize connection to '%.*s' with schema path '%.*s' and keys '%.*s'\n",
             id->url.len, id->url.s,
-            redis_schema.len, redis_schema.s,
+            redis_schema_path.len, redis_schema_path.s,
             redis_keys.len, redis_keys.s);
     */
     LM_DBG("trying to initialize connection to '%.*s'\n",
@@ -209,12 +211,14 @@ void *db_redis_command_argv(km_redis_con_t *con, redis_key_t *query) {
     LM_DBG("query has %d args\n", argc);
 
     redisReply *reply = redisCommandArgv(con->con, argc, (const char**)argv, NULL);
-    if (con->con->err == REDIS_ERR_EOF &&
-        strcmp(con->con->errstr,"Server closed the connection") == 0) {
-
+    if (con->con->err == REDIS_ERR_EOF) {
         if (db_redis_connect(con) != 0) {
             LM_ERR("Failed to reconnect to redis db\n");
             pkg_free(argv);
+            if (con->con) {
+                redisFree(con->con);
+                con->con = NULL;
+            }
             return NULL;
         }
         reply = redisCommandArgv(con->con, argc, (const char**)argv, NULL);
@@ -237,12 +241,14 @@ int db_redis_append_command_argv(km_redis_con_t *con, redis_key_t *query) {
     LM_DBG("query has %d args\n", argc);
 
     ret = redisAppendCommandArgv(con->con, argc, (const char**)argv, NULL);
-    if (con->con->err == REDIS_ERR_EOF &&
-        strcmp(con->con->errstr,"Server closed the connection") == 0) {
-
+    if (con->con->err == REDIS_ERR_EOF) {
         if (db_redis_connect(con) != 0) {
             LM_ERR("Failed to reconnect to redis db\n");
             pkg_free(argv);
+            if (con->con) {
+                redisFree(con->con);
+                con->con = NULL;
+            }
             return ret;
         }
         ret = redisAppendCommandArgv(con->con, argc, (const char**)argv, NULL);
@@ -259,11 +265,13 @@ int db_redis_get_reply(km_redis_con_t *con, void **reply) {
 
     *reply = NULL;
     ret = redisGetReply(con->con, reply);
-    if (con->con->err == REDIS_ERR_EOF &&
-        strcmp(con->con->errstr,"Server closed the connection") == 0) {
-
+    if (con->con->err == REDIS_ERR_EOF) {
         if (db_redis_connect(con) != 0) {
             LM_ERR("Failed to reconnect to redis db\n");
+            if (con->con) {
+                redisFree(con->con);
+                con->con = NULL;
+            }
             return ret;
         }
         ret = redisGetReply(con->con, reply);

+ 82 - 12
src/modules/db_redis/redis_dbase.c

@@ -64,6 +64,16 @@ void db_redis_close(db1_con_t* _h) {
     db_do_close(_h, db_redis_free_connection);
 }
 
+
+static db1_res_t* db_redis_new_result(void) {
+    db1_res_t* obj;
+
+    obj = db_new_result();
+    if (!obj)
+        return NULL;
+    return obj;
+}
+
 static int db_redis_val2str(const db_val_t *v, str *_str) {
     const char *s;
     const str *tmpstr;
@@ -148,6 +158,64 @@ err:
     return -1;
 }
 
+static int db_redis_return_version(const db1_con_t* _h, km_redis_con_t *con, const str *table_name,
+    db1_res_t **_r) {
+
+    struct str_hash_entry *table_e;
+    redis_table_t *table;
+    db_val_t* dval;
+    db_row_t* drow;
+
+    LM_DBG("get table version\n");
+
+    table_e = str_hash_get(&con->tables, table_name->s, table_name->len);
+    if (!table_e) {
+        LM_ERR("query to undefined table '%.*s', define it in schema file!\n",
+                table_name->len, table_name->s);
+        return -1;
+    }
+    table = (redis_table_t*)table_e->u.p;
+
+    *_r = db_redis_new_result();
+    if (!*_r) {
+        LM_ERR("Failed to allocate memory for result");
+        return -1;
+    }
+    RES_NUM_ROWS(*_r) = 1;
+    RES_COL_N(*_r) = 1;
+    RES_ROW_N(*_r) = 1;
+    if (db_allocate_rows(*_r) != 0) {
+        LM_ERR("Failed to allocate memory for rows\n");
+        goto err;
+    }
+    if (db_allocate_columns(*_r, 1) != 0) {
+        LM_ERR("Failed to allocate memory for result columns");
+        goto err;
+    }
+
+    drow = &(RES_ROWS(*_r)[0]);
+
+    if (db_allocate_row(*_r, drow) != 0) {
+        LM_ERR("Failed to allocate row %d\n", RES_NUM_ROWS(*_r));
+        goto err;
+    }
+
+    dval = &(ROW_VALUES(drow)[0]);
+
+    VAL_TYPE(dval) = DB1_INT;
+    VAL_NULL(dval) = 0;
+    VAL_INT(dval) = table->version;
+
+    LM_DBG("returning short-cut table version %d for table '%.*s'",
+        table->version, table_name->len, table_name->s);
+
+    return 0;
+
+err:
+    if (*_r) db_redis_free_result((db1_con_t*)_h, *_r);
+    return -1;
+}
+
 static int db_redis_build_entry_manual_keys(redis_table_t *table, const db_key_t *_k, const db_val_t *_v, const int _n, int **manual_keys, int *manual_key_count) {
 
     // TODO: we also put keys here which are already part of type mapping!
@@ -680,15 +748,6 @@ err:
     return -1;
 }
 
-static db1_res_t* db_redis_new_result(void) {
-    db1_res_t* obj;
-
-    obj = db_new_result();
-    if (!obj)
-        return NULL;
-    return obj;
-}
-
 static int db_redis_compare_column(db_key_t k, db_val_t *v, db_op_t op, redisReply *reply) {
     int i_value;
     long long ll_value;
@@ -1001,9 +1060,6 @@ static int db_redis_perform_query(const db1_con_t* _h, km_redis_con_t *con, cons
         }
     }
 
-
-
-    LM_DBG("++++++ going over query keys\n");
     for (redis_key_t *key = *keys; key; key = key->next) {
         redis_key_t *tmp = NULL;
         str *keyname = &(key->key);
@@ -1609,6 +1665,20 @@ int db_redis_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op,
     }
 
     if(_r) *_r = NULL;
+
+    // check if we have a version query, and return version directly from
+    // schema instead of loading it from redis
+    if (_nc == 1 && _n >= 1 && VAL_TYPE(&_v[0]) == DB1_STR &&
+            CON_TABLE(_h)->len == strlen("version") && strncmp(CON_TABLE(_h)->s, "version", CON_TABLE(_h)->len) == 0 &&
+            _k[0]->len == strlen("table_name") && strncmp(_k[0]->s, "table_name", _k[0]->len) == 0 &&
+            _c[0]->len == strlen("table_version") && strncmp(_c[0]->s, "table_version", _c[0]->len) == 0) {
+
+        if (db_redis_return_version(_h, con, &VAL_STR(&_v[0]), _r) == 0) {
+            return 0;
+        }
+        // if we fail to return a version from the schema, go query the table, just in case
+    }
+
     free_op = 0;
 
     if (_op == NULL) {

+ 244 - 98
src/modules/db_redis/redis_table.c

@@ -19,6 +19,8 @@
  */
 
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
 
 #include "db_redis_mod.h"
 #include "redis_connection.h"
@@ -148,7 +150,6 @@ int db_redis_key_list2arr(redis_key_t *list, char ***arr) {
     }
     for (tmp = list, i = 0; tmp; tmp = tmp->next, i++) {
         (*arr)[i] = tmp->key.s;
-        LM_DBG("++++++ assign #%d (%s) from list to array\n", i, (*arr)[i]);
     }
     LM_DBG("returning %d entries\n", len);
 
@@ -308,6 +309,7 @@ void db_redis_free_tables(km_redis_con_t *con) {
             for (int j = 0; j < col_ht->size; ++j) {
                 col_last = (&col_ht->table[j])->prev;
                 clist_foreach(&col_ht->table[j], col_he, next) {
+                    pkg_free(col_he->key.s);
                     pkg_free(col_he);
                     if (col_he == col_last) break;
                 }
@@ -334,6 +336,7 @@ void db_redis_free_tables(km_redis_con_t *con) {
                 pkg_free(tmptype);
             }
             pkg_free(table);
+            pkg_free(he->key.s);
             pkg_free(he);
 
             if (he == last) break;
@@ -372,6 +375,9 @@ static redis_type_t* db_redis_create_type(str *type) {
 static struct str_hash_entry* db_redis_create_table(str *table) {
     struct str_hash_entry *e;
     redis_table_t *t;
+
+    LM_DBG("creating schema hash entry for table '%.*s'", table->len, table->s);
+
     e = (struct str_hash_entry*) pkg_malloc(sizeof(struct str_hash_entry));
     if (!e) {
         LM_ERR("Failed to allocate memory for table entry\n");
@@ -379,13 +385,17 @@ static struct str_hash_entry* db_redis_create_table(str *table) {
     }
     memset(e, 0, sizeof(struct str_hash_entry));
 
-    e->key.s = table->s;
-    e->key.len = table->len;
+    if (pkg_str_dup(&e->key, table) != 0) {
+        LM_ERR("Failed to allocate memory for table name\n");
+        pkg_free(e);
+        return NULL;
+    }
     e->flags = 0;
 
     t = (redis_table_t*) pkg_malloc(sizeof(redis_table_t));
     if (!t) {
         LM_ERR("Failed to allocate memory for table data\n");
+        pkg_free(e->key.s);
         pkg_free(e);
         return NULL;
     }
@@ -394,6 +404,7 @@ static struct str_hash_entry* db_redis_create_table(str *table) {
 
     if (str_hash_alloc(&t->columns, REDIS_HT_SIZE) != 0) {
         LM_ERR("Failed to allocate memory for table schema hashtable\n");
+        pkg_free(e->key.s);
         pkg_free(e);
         return NULL;
     }
@@ -410,8 +421,10 @@ static struct str_hash_entry* db_redis_create_column(str *col, str *type) {
         LM_ERR("Failed to allocate memory for column entry\n");
         return NULL;
     }
-    e->key.s = col->s;
-    e->key.len = col->len;
+    if (pkg_str_dup(&e->key, col) != 0) {
+        LM_ERR("Failed to allocate memory for column name\n");
+        return NULL;
+    }
     e->flags = 0;
     switch (type->s[0]) {
         case 's':
@@ -467,12 +480,13 @@ int db_redis_parse_keys(km_redis_con_t *con) {
         DBREDIS_KEYS_END_ST
     } state;
 
-    //LM_DBG("parsing keys '%.*s'\n", redis_keys.len, redis_keys.s);
     if (!redis_keys.len) {
         LM_ERR("Failed to parse empty 'keys' mod-param, please define it!\n");
         return -1;
     }
 
+    type_target = NULL;
+    key_location = NULL;
     end = redis_keys.s + redis_keys.len;
     p = start = redis_keys.s;
     state = DBREDIS_KEYS_TABLE_ST;
@@ -489,7 +503,7 @@ int db_redis_parse_keys(km_redis_con_t *con) {
                 table_name.len = p - start;
                 state = DBREDIS_KEYS_TYPE_ST;
                 start = ++p;
-                //LM_DBG("found table name '%.*s'\n", table_name.len, table_name.s);
+                LM_DBG("found table name '%.*s'\n", table_name.len, table_name.s);
 
                 table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
                 if (!table_entry) {
@@ -510,9 +524,9 @@ int db_redis_parse_keys(km_redis_con_t *con) {
                 type_name.len = p - start;
                 state = DBREDIS_KEYS_COLUMN_ST;
                 start = ++p;
-                //LM_DBG("found type name '%.*s' for table '%.*s'\n",
-                //        type_name.len, type_name.s,
-                //        table_name.len, table_name.s);
+                LM_DBG("found type name '%.*s' for table '%.*s'\n",
+                        type_name.len, type_name.s,
+                        table_name.len, table_name.s);
                 if (type_name.len == REDIS_DIRECT_PREFIX_LEN &&
                         !strncmp(type_name.s, REDIS_DIRECT_PREFIX, type_name.len)) {
                     key_target = &table->entry_keys;
@@ -543,11 +557,12 @@ int db_redis_parse_keys(km_redis_con_t *con) {
                 column_name.s = start;
                 column_name.len = p - start;
                 start = ++p;
-                //LM_DBG("found column name '%.*s' in type '%.*s' for table '%.*s'\n",
-                //        column_name.len, column_name.s,
-                //        type_name.len, type_name.s,
-                //        table_name.len, table_name.s);
-
+                /*
+                LM_DBG("found column name '%.*s' in type '%.*s' for table '%.*s'\n",
+                        column_name.len, column_name.s,
+                        type_name.len, type_name.s,
+                        table_name.len, table_name.s);
+                */
                 key = db_redis_create_key(&column_name);
                 if (!key) goto err;
                 if (*key_target == NULL) {
@@ -558,7 +573,7 @@ int db_redis_parse_keys(km_redis_con_t *con) {
                 }
                 break;
             case DBREDIS_KEYS_END_ST:
-                //LM_DBG("done parsing keys definition\n");
+                LM_DBG("done parsing keys definition\n");
                 return 0;
 
 
@@ -574,9 +589,10 @@ err:
 
 
 int db_redis_parse_schema(km_redis_con_t *con) {
-    char *p;
-    char *start;
-    char *end;
+    DIR *srcdir;
+    FILE *fin;
+    struct dirent* dent;
+    char *dir_name;
 
     str table_name;
     str column_name;
@@ -586,107 +602,237 @@ int db_redis_parse_schema(km_redis_con_t *con) {
     struct str_hash_entry *column_entry;
     redis_table_t *table;
 
+    char full_path[_POSIX_PATH_MAX + 1];
+    int path_len;
+    struct stat fstat;
+    char c;
+
     enum {
-        DBREDIS_SCHEMA_TABLE_ST,
         DBREDIS_SCHEMA_COLUMN_ST,
         DBREDIS_SCHEMA_TYPE_ST,
-        DBREDIS_SCHEMA_END_ST
+        DBREDIS_SCHEMA_VERSION_ST,
+        DBREDIS_SCHEMA_END_ST,
     } state;
 
+    char buf[4096];
+    char *bufptr;
+
+
+    srcdir = NULL;
+    fin = NULL;
+    dir_name = NULL;
+
     //LM_DBG("parsing schema '%.*s'\n", redis_schema.len, redis_schema.s);
-    if (!redis_schema.len) {
-        LM_ERR("Failed to parse empty 'schema' mod-param, please define it!\n");
+    if (!redis_schema_path.len) {
+        LM_ERR("Failed to parse empty 'schema_path' mod-param, please define it!\n");
         return -1;
     }
 
+    dir_name = (char*)pkg_malloc((redis_schema_path.len + 1) * sizeof(char));
+    strncpy(dir_name, redis_schema_path.s, redis_schema_path.len);
+    dir_name[redis_schema_path.len] = '\0';
+    srcdir = opendir(dir_name);
+    if (!srcdir) {
+        LM_ERR("Failed to open schema directory '%s'\n", dir_name);
+        goto err;
+    }
+
     if (str_hash_alloc(&con->tables, REDIS_HT_SIZE) != 0) {
         LM_ERR("Failed to allocate memory for tables hashtable\n");
         goto err;
     }
     str_hash_init(&con->tables);
 
-    end = redis_schema.s + redis_schema.len;
-    p = start = redis_schema.s;
-    state = DBREDIS_SCHEMA_TABLE_ST;
-    do {
-        switch(state) {
-            case DBREDIS_SCHEMA_TABLE_ST:
-                while(p != end && *p != '=')
-                    ++p;
-                if (p == end) {
-                    LM_ERR("Invalid table definition, expecting <table>=<definition>\n");
-                    goto err;
-                }
-                table_name.s = start;
-                table_name.len = p - start;
-                state = DBREDIS_SCHEMA_COLUMN_ST;
-                start = ++p;
-                //LM_DBG("found table name '%.*s'\n", table_name.len, table_name.s);
+    while((dent = readdir(srcdir)) != NULL) {
+        path_len = redis_schema_path.len;
+        if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
+            continue;
+        }
+        if ((path_len + strlen(dent->d_name) + 1) > _POSIX_PATH_MAX) {
+            LM_WARN("Redis schema path '%.*s/%s' is too long, skipping...\n",
+                redis_schema_path.len, redis_schema_path.s, dent->d_name);
+            continue;
+        }
+        snprintf(full_path, sizeof(full_path), "%s/%s", dir_name, dent->d_name);
 
-                table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
-                if (table_entry) {
-                    LM_ERR("Found duplicate table schema definition '%.*s', fix config by removing one from the 'schema' mod-param!\n",
-                            table_name.len, table_name.s);
-                    goto err;
-                }
-                table_entry = db_redis_create_table(&table_name);
-                if (!table_entry) goto err;
-                str_hash_add(&con->tables, table_entry);
-                table = table_entry->u.p;
-                break;
-            case DBREDIS_SCHEMA_COLUMN_ST:
-                while(p != end && *p != '/')
-                    ++p;
-                if (p == end) {
-                    LM_ERR("Invalid column definition, expecting <column>/<type>\n");
-                    goto err;
-                }
-                column_name.s = start;
-                column_name.len = p - start;
-                state = DBREDIS_SCHEMA_TYPE_ST;
-                start = ++p;
-                //LM_DBG("found column name '%.*s'\n", column_name.len, column_name.s);
-                break;
-            case DBREDIS_SCHEMA_TYPE_ST:
-                while(p != end && *p != ',' && *p != ';')
-                    ++p;
-                type_name.s = start;
-                type_name.len = p - start;
-                if (p == end) {
-                    state = DBREDIS_SCHEMA_END_ST;
-                } else if (*p == ';') {
-                    state = DBREDIS_SCHEMA_TABLE_ST;
-                } else {
-                    state = DBREDIS_SCHEMA_COLUMN_ST;
-                }
-                start = ++p;
-                //LM_DBG("found column type '%.*s' for column name '%.*s' in table '%.*s'\n",
-                //        type_name.len, type_name.s,
-                //        column_name.len, column_name.s,
-                //        table_name.len, table_name.s);
-
-                column_entry = str_hash_get(&table->columns, column_name.s, column_name.len);
-                if (column_entry) {
-                    LM_ERR("Found duplicate column definition '%.*s' in schema definition of table '%.*s', remove one from mod-param 'schema'!\n",
+        if (stat(full_path, &fstat) < 0) {
+            LM_ERR("Failed to stat schema file %s\n", full_path);
+            continue;
+        }
+        if (!S_ISREG(fstat.st_mode)) {
+            LM_DBG("skipping schema file '%s' as it's not a regular file\n", full_path);
+            continue;
+        }
+
+        LM_DBG("reading schema full path '%s'\n", full_path);
+
+        fin = fopen(full_path, "r");
+        if (!fin) {
+            LM_ERR("Failed to open redis schema file '%s'\n", full_path);
+            continue;
+        }
+
+        table_name.s = dent->d_name;
+        table_name.len = strlen(table_name.s);
+        table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
+        if (table_entry) {
+            // should not happen, as this would require two files with same name
+            LM_WARN("Found duplicate table schema definition '%.*s', skipping...\n",
+                    table_name.len, table_name.s);
+            fclose(fin);
+            continue;
+        }
+        table_entry = db_redis_create_table(&table_name);
+        if (!table_entry) goto err;
+        str_hash_add(&con->tables, table_entry);
+        table = table_entry->u.p;
+
+        state = DBREDIS_SCHEMA_COLUMN_ST;
+        memset(buf, 0, sizeof(buf));
+        bufptr = buf;
+        do {
+            if (bufptr - buf > sizeof(buf)) {
+                LM_ERR("Schema line too long in file %s\n", full_path);
+                goto err;
+            }
+
+            c = fgetc(fin);
+
+            if (c == '\r')
+                continue;
+            //LM_DBG("parsing char %c, buf is '%s' at pos %lu\n", c, buf, bufpos);
+            switch(state) {
+                case DBREDIS_SCHEMA_COLUMN_ST:
+                    if (c == EOF) {
+                        LM_ERR("Unexpected end of file in schema column name of file %s\n", full_path);
+                        goto err;
+                    }
+                    if(c != '\n' && c != '/') {
+                        *bufptr = c;
+                        bufptr++;
+                        continue;
+                    }
+                    if (c == '\n') {
+                        if (bufptr == buf) {
+                            // trailing comma, skip
+                            state = DBREDIS_SCHEMA_VERSION_ST;
+                            continue;
+                        } else {
+                            LM_ERR("Invalid column definition, expecting <column>/<type>\n");
+                            goto err;
+                        }
+                    }
+                    column_name.s = buf;
+                    column_name.len = bufptr - buf;
+                    bufptr++;
+                    state = DBREDIS_SCHEMA_TYPE_ST;
+                    LM_DBG("found column name '%.*s'\n", column_name.len, column_name.s);
+                    break;
+                case DBREDIS_SCHEMA_TYPE_ST:
+                    if (c == EOF) {
+                        LM_ERR("Unexpected end of file in schema column type of file %s\n", full_path);
+                        goto err;
+                    }
+                    if(c != '\n' && c != ',') {
+                        *bufptr = c;
+                        bufptr++;
+                        continue;
+                    }
+                    type_name.s = buf + column_name.len + 1;
+                    type_name.len = bufptr - type_name.s;
+
+                    if (c == '\n') {
+                        state = DBREDIS_SCHEMA_VERSION_ST;
+                    } else {
+                        state = DBREDIS_SCHEMA_COLUMN_ST;
+                    }
+                    /*
+                    LM_DBG("found column type '%.*s' with len %d for column name '%.*s' in table '%.*s'\n",
+                            type_name.len, type_name.s,
+                            type_name.len,
                             column_name.len, column_name.s,
                             table_name.len, table_name.s);
-                    goto err;
-                }
-                column_entry = db_redis_create_column(&column_name, &type_name);
-                if (!column_entry) {
-                    goto err;
-                }
-                str_hash_add(&table->columns, column_entry);
-                break;
-            case DBREDIS_SCHEMA_END_ST:
-                //LM_DBG("done parsing redis table schema\n");
-                return 0;
+                    */
+                    column_entry = str_hash_get(&table->columns, column_name.s, column_name.len);
+                    if (column_entry) {
+                        LM_ERR("Found duplicate column definition '%.*s' in schema definition of table '%.*s', remove one from schema!\n",
+                                column_name.len, column_name.s,
+                                table_name.len, table_name.s);
+                        goto err;
+                    }
+                    column_entry = db_redis_create_column(&column_name, &type_name);
+                    if (!column_entry) {
+                        goto err;
+                    }
+                    str_hash_add(&table->columns, column_entry);
+                    memset(buf, 0, sizeof(buf));
+                    bufptr = buf;
+                    break;
+                case DBREDIS_SCHEMA_VERSION_ST:
+                    if (c != '\n' && c != EOF) {
+                        *bufptr = c;
+                        bufptr++;
+                        continue;
+                    }
+                    *bufptr = '\0';
+                    table->version = atoi(buf);
+                    state = DBREDIS_SCHEMA_END_ST;
+                    break;
+                case DBREDIS_SCHEMA_END_ST:
+                    goto fileend;
+                    break;
+            }
+        } while (c != EOF);
 
-        }
-    } while (p != end);
+fileend:
+        fclose(fin);
+        fin = NULL;
+    }
+
+
+    closedir(srcdir);
+    pkg_free(dir_name);
 
     return 0;
 err:
+    if (fin)
+        fclose(fin);
+    if (srcdir)
+        closedir(srcdir);
+    if (dir_name)
+        pkg_free(dir_name);
+
     db_redis_free_tables(con);
     return -1;
 }
+
+int db_redis_keys_spec(char *spec) {
+    size_t len = strlen(spec);
+
+    if (redis_keys.len == 0) {
+        redis_keys.s = (char*)pkg_malloc(len * sizeof(char));
+        if (!redis_keys.s) {
+            LM_ERR("Failed to allocate memory for keys spec\n");
+            goto err;
+        }
+    } else {
+        redis_keys.s = (char*)pkg_realloc(redis_keys.s, redis_keys.len + 1 + len);
+        if (!redis_keys.s) {
+            LM_ERR("Failed to reallocate memory for keys spec\n");
+            goto err;
+        }
+        redis_keys.s[redis_keys.len] = ';';
+        redis_keys.len++;
+    }
+
+    strncpy(redis_keys.s + redis_keys.len, spec, len);
+    redis_keys.len += len;
+
+    return 0;
+
+err:
+    if (redis_keys.len) {
+        pkg_free(redis_keys.s);
+    }
+    return -1;
+}

+ 3 - 0
src/modules/db_redis/redis_table.h

@@ -41,6 +41,7 @@ struct redis_type {
 
 typedef struct redis_table redis_table_t;
 struct redis_table {
+	int version;
     redis_key_t *entry_keys;
     redis_type_t *types;
     struct str_hash_table columns;
@@ -60,4 +61,6 @@ int db_redis_key_list2arr(redis_key_t *list, char ***arr);
 redis_key_t * db_redis_key_unshift(redis_key_t **list);
 void db_redis_key_free(redis_key_t **list);
 
+int db_redis_keys_spec(char *spec);
+
 #endif /* _REDIS_TABLE_H_ */

+ 2 - 0
utils/kamctl/db_redis/kamailio/acc

@@ -0,0 +1,2 @@
+id/int,method/string,from_tag/string,to_tag/string,callid/string,sip_code/string,sip_reason/string,time/int,
+5

+ 2 - 0
utils/kamctl/db_redis/kamailio/acc_cdrs

@@ -0,0 +1,2 @@
+id/int,start_time/int,end_time/int,duration/double,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/active_watchers

@@ -0,0 +1,2 @@
+id/int,presentity_uri/string,watcher_username/string,watcher_domain/string,to_user/string,to_domain/string,event/string,event_id/string,to_tag/string,from_tag/string,callid/string,local_cseq/int,remote_cseq/int,contact/string,record_route/string,expires/int,status/int,reason/string,version/int,socket_info/string,local_contact/string,from_user/string,from_domain/string,updated/int,updated_winfo/int,flags/int,user_agent/string,
+12

+ 2 - 0
utils/kamctl/db_redis/kamailio/address

@@ -0,0 +1,2 @@
+id/int,grp/int,ip_addr/string,mask/int,port/int,tag/string,
+6

+ 2 - 0
utils/kamctl/db_redis/kamailio/aliases

@@ -0,0 +1,2 @@
+id/int,ruid/string,username/string,domain/string,contact/string,received/string,path/string,expires/int,q/double,callid/string,cseq/int,last_modified/int,flags/int,cflags/int,user_agent/string,socket/string,methods/int,instance/string,reg_id/int,server_id/int,connection_id/int,keepalive/int,partition/int,
+8

+ 2 - 0
utils/kamctl/db_redis/kamailio/carrier_name

@@ -0,0 +1,2 @@
+id/int,carrier/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/carrierfailureroute

@@ -0,0 +1,2 @@
+id/int,carrier/int,domain/int,scan_prefix/string,host_name/string,reply_code/string,flags/int,mask/int,next_domain/int,description/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/carrierroute

@@ -0,0 +1,2 @@
+id/int,carrier/int,domain/int,scan_prefix/string,flags/int,mask/int,prob/double,strip/int,rewrite_host/string,rewrite_prefix/string,rewrite_suffix/string,description/string,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/cpl

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,cpl_xml/string,cpl_bin/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/dbaliases

@@ -0,0 +1,2 @@
+id/int,alias_username/string,alias_domain/string,username/string,domain/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/dialog

@@ -0,0 +1,2 @@
+id/int,hash_entry/int,hash_id/int,callid/string,from_uri/string,from_tag/string,to_uri/string,to_tag/string,caller_cseq/string,callee_cseq/string,caller_route_set/string,callee_route_set/string,caller_contact/string,callee_contact/string,caller_sock/string,callee_sock/string,state/int,start_time/int,timeout/int,sflags/int,iflags/int,toroute_name/string,req_uri/string,xdata/string,
+7

+ 2 - 0
utils/kamctl/db_redis/kamailio/dialog_vars

@@ -0,0 +1,2 @@
+id/int,hash_entry/int,hash_id/int,dialog_key/string,dialog_value/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/dialplan

@@ -0,0 +1,2 @@
+id/int,dpid/int,pr/int,match_op/int,match_exp/string,match_len/int,subst_exp/string,repl_exp/string,attrs/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/dispatcher

@@ -0,0 +1,2 @@
+id/int,setid/int,destination/string,flags/int,priority/int,attrs/string,description/string,
+4

+ 2 - 0
utils/kamctl/db_redis/kamailio/domain

@@ -0,0 +1,2 @@
+id/int,domain/string,did/string,last_modified/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/domain_attrs

@@ -0,0 +1,2 @@
+id/int,did/string,name/string,type/int,value/string,last_modified/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/domain_name

@@ -0,0 +1,2 @@
+id/int,domain/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/domainpolicy

@@ -0,0 +1,2 @@
+id/int,rule/string,type/string,att/string,val/string,description/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/dr_gateways

@@ -0,0 +1,2 @@
+gwid/int,type/int,address/string,strip/int,pri_prefix/string,attrs/string,description/string,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/dr_groups

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,groupid/int,description/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/dr_gw_lists

@@ -0,0 +1,2 @@
+id/int,gwlist/string,description/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/dr_rules

@@ -0,0 +1,2 @@
+ruleid/int,groupid/string,prefix/string,timerec/string,priority/int,routeid/string,gwlist/string,description/string,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/globalblacklist

@@ -0,0 +1,2 @@
+id/int,prefix/string,whitelist/int,description/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/grp

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,grp/string,last_modified/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/htable

@@ -0,0 +1,2 @@
+id/int,key_name/string,key_type/int,value_type/int,key_value/string,expires/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/imc_members

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,room/string,flag/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/imc_rooms

@@ -0,0 +1,2 @@
+id/int,name/string,domain/string,flag/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/lcr_gw

@@ -0,0 +1,2 @@
+id/int,lcr_id/int,gw_name/string,ip_addr/string,hostname/string,port/int,params/string,uri_scheme/int,transport/int,strip/int,prefix/string,tag/string,flags/int,defunct/int,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/lcr_rule

@@ -0,0 +1,2 @@
+id/int,lcr_id/int,prefix/string,from_uri/string,request_uri/string,stopper/int,enabled/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/lcr_rule_target

@@ -0,0 +1,2 @@
+id/int,lcr_id/int,rule_id/int,gw_id/int,priority/int,weight/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/location

@@ -0,0 +1,2 @@
+id/int,ruid/string,username/string,domain/string,contact/string,received/string,path/string,expires/int,q/double,callid/string,cseq/int,last_modified/int,flags/int,cflags/int,user_agent/string,socket/string,methods/int,instance/string,reg_id/int,server_id/int,connection_id/int,keepalive/int,partition/int,
+8

+ 2 - 0
utils/kamctl/db_redis/kamailio/location_attrs

@@ -0,0 +1,2 @@
+id/int,ruid/string,username/string,domain/string,aname/string,atype/int,avalue/string,last_modified/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/matrix

@@ -0,0 +1,2 @@
+first/int,second/int,res/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/missed_calls

@@ -0,0 +1,2 @@
+id/int,method/string,from_tag/string,to_tag/string,callid/string,sip_code/string,sip_reason/string,time/int,
+4

+ 2 - 0
utils/kamctl/db_redis/kamailio/mohqcalls

@@ -0,0 +1,2 @@
+id/int,mohq_id/int,call_id/string,call_status/int,call_from/string,call_contact/string,call_time/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/mohqueues

@@ -0,0 +1,2 @@
+id/int,name/string,uri/string,mohdir/string,mohfile/string,debug/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/mtree

@@ -0,0 +1,2 @@
+id/int,tprefix/string,tvalue/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/mtrees

@@ -0,0 +1,2 @@
+id/int,tname/string,tprefix/string,tvalue/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/pdt

@@ -0,0 +1,2 @@
+id/int,sdomain/string,prefix/string,domain/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/pl_pipes

@@ -0,0 +1,2 @@
+id/int,pipeid/string,algorithm/string,plimit/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/presentity

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,event/string,etag/string,expires/int,received_time/int,body/string,sender/string,priority/int,
+4

+ 2 - 0
utils/kamctl/db_redis/kamailio/pua

@@ -0,0 +1,2 @@
+id/int,pres_uri/string,pres_id/string,event/int,expires/int,desired_expires/int,flag/int,etag/string,tuple_id/string,watcher_uri/string,call_id/string,to_tag/string,from_tag/string,cseq/int,record_route/string,contact/string,remote_contact/string,version/int,extra_headers/string,
+7

+ 2 - 0
utils/kamctl/db_redis/kamailio/purplemap

@@ -0,0 +1,2 @@
+id/int,sip_user/string,ext_user/string,ext_prot/string,ext_pass/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/re_grp

@@ -0,0 +1,2 @@
+id/int,reg_exp/string,group_id/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/rls_presentity

@@ -0,0 +1,2 @@
+id/int,rlsubs_did/string,resource_uri/string,content_type/string,presence_state/string,expires/int,updated/int,auth_state/int,reason/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/rls_watchers

@@ -0,0 +1,2 @@
+id/int,presentity_uri/string,to_user/string,to_domain/string,watcher_username/string,watcher_domain/string,event/string,event_id/string,to_tag/string,from_tag/string,callid/string,local_cseq/int,remote_cseq/int,contact/string,record_route/string,expires/int,status/int,reason/string,version/int,socket_info/string,local_contact/string,from_user/string,from_domain/string,updated/int,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/rtpengine

@@ -0,0 +1,2 @@
+setid/int,url/string,weight/int,disabled/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/rtpproxy

@@ -0,0 +1,2 @@
+id/int,setid/string,url/string,flags/int,weight/int,description/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/sca_subscriptions

@@ -0,0 +1,2 @@
+id/int,subscriber/string,aor/string,event/int,expires/int,state/int,app_idx/int,call_id/string,from_tag/string,to_tag/string,record_route/string,notify_cseq/int,subscribe_cseq/int,server_id/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/silo

@@ -0,0 +1,2 @@
+id/int,src_addr/string,dst_addr/string,username/string,domain/string,inc_time/int,exp_time/int,snd_time/int,ctype/string,body/string,extra_hdrs/string,callid/string,status/int,
+8

+ 2 - 0
utils/kamctl/db_redis/kamailio/sip_trace

@@ -0,0 +1,2 @@
+id/int,time_stamp/int,time_us/int,callid/string,traced_user/string,msg/string,method/string,status/string,fromip/string,toip/string,fromtag/string,totag/string,direction/string,
+4

+ 2 - 0
utils/kamctl/db_redis/kamailio/speed_dial

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,sd_username/string,sd_domain/string,new_uri/string,fname/string,lname/string,description/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/subscriber

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,password/string,email_address/string,ha1/string,ha1b/string,rpid/string,
+6

+ 2 - 0
utils/kamctl/db_redis/kamailio/topos_d

@@ -0,0 +1,2 @@
+id/int,rectime/int,s_method/string,s_cseq/string,a_callid/string,a_uuid/string,b_uuid/string,a_contact/string,b_contact/string,as_contact/string,bs_contact/string,a_tag/string,b_tag/string,a_rr/string,b_rr/string,s_rr/string,iflags/int,a_uri/string,b_uri/string,r_uri/string,a_srcaddr/string,b_srcaddr/string,a_socket/string,b_socket/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/topos_t

@@ -0,0 +1,2 @@
+id/int,rectime/int,s_method/string,s_cseq/string,a_callid/string,a_uuid/string,b_uuid/string,direction/int,x_via/string,x_vbranch/string,x_rr/string,y_rr/string,s_rr/string,x_uri/string,a_contact/string,b_contact/string,as_contact/string,bs_contact/string,x_tag/string,a_tag/string,b_tag/string,a_srcaddr/string,b_srcaddr/string,a_socket/string,b_socket/string,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/trusted

@@ -0,0 +1,2 @@
+id/int,src_ip/string,proto/string,from_pattern/string,ruri_pattern/string,tag/string,priority/int,
+6

+ 2 - 0
utils/kamctl/db_redis/kamailio/uacreg

@@ -0,0 +1,2 @@
+id/int,l_uuid/string,l_username/string,l_domain/string,r_username/string,r_domain/string,realm/string,auth_username/string,auth_password/string,auth_proxy/string,expires/int,flags/int,reg_delay/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_credentials

@@ -0,0 +1,2 @@
+id/int,auth_username/string,did/string,realm/string,password/string,flags/int,ha1/string,ha1b/string,uid/string,
+7

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_domain

@@ -0,0 +1,2 @@
+id/int,did/string,domain/string,flags/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_domain_attrs

@@ -0,0 +1,2 @@
+id/int,did/string,name/string,type/int,value/string,flags/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_global_attrs

@@ -0,0 +1,2 @@
+id/int,name/string,type/int,value/string,flags/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_uri

@@ -0,0 +1,2 @@
+id/int,uid/string,did/string,username/string,flags/int,scheme/string,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_uri_attrs

@@ -0,0 +1,2 @@
+id/int,username/string,did/string,name/string,value/string,type/int,flags/int,scheme/string,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/uid_user_attrs

@@ -0,0 +1,2 @@
+id/int,uid/string,name/string,value/string,type/int,flags/int,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/uri

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,uri_user/string,last_modified/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/userblacklist

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,prefix/string,whitelist/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/usr_preferences

@@ -0,0 +1,2 @@
+id/int,uuid/string,username/string,domain/string,attribute/string,type/int,value/string,last_modified/int,
+2

+ 2 - 0
utils/kamctl/db_redis/kamailio/version

@@ -0,0 +1,2 @@
+table_name/string,table_version/int,
+1

+ 2 - 0
utils/kamctl/db_redis/kamailio/watchers

@@ -0,0 +1,2 @@
+id/int,presentity_uri/string,watcher_username/string,watcher_domain/string,event/string,status/int,reason/string,inserted_time/int,
+3

+ 2 - 0
utils/kamctl/db_redis/kamailio/xcap

@@ -0,0 +1,2 @@
+id/int,username/string,domain/string,doc/string,doc_type/int,etag/string,source/int,doc_uri/string,port/int,
+4