redis: init variables to get rid of compile warnings
[sip-router] / src / modules / db_redis / redis_table.c
1 /*
2  * Copyright (C) 2018 Andreas Granig (sipwise.com)
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include <stdlib.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24
25 #include "db_redis_mod.h"
26 #include "redis_connection.h"
27 #include "redis_table.h"
28
29 int db_redis_key_add_string(redis_key_t **list, const char* entry, int len) {
30     redis_key_t *k;
31
32
33     k = (redis_key_t*)pkg_malloc(sizeof(redis_key_t));
34     if (!k) {
35         LM_ERR("Failed to allocate memory for key list entry\n");
36         goto err;
37     }
38     k->next = NULL;
39
40     k->key.s = (char*)pkg_malloc((len+1) * sizeof(char));
41     if (!k->key.s) {
42         LM_ERR("Failed to allocate memory for key list entry\n");
43         goto err;
44     }
45
46     memcpy(k->key.s, entry, len);
47     k->key.s[len] = '\0';
48     k->key.len = len;
49
50     if (!*list) {
51         *list = k;
52     } else {
53         redis_key_t *l = *list;
54         while (l->next)
55             l = l->next;
56         l->next = k;
57     }
58
59     return 0;
60
61 err:
62     if (k)
63         pkg_free(k);
64     return -1;
65 }
66
67 int db_redis_key_add_str(redis_key_t **list, const str* entry) {
68     return db_redis_key_add_string(list, entry->s, entry->len);
69 }
70
71 int db_redis_key_prepend_string(redis_key_t **list, const char* entry, int len) {
72     redis_key_t *k;
73
74     k = (redis_key_t*)pkg_malloc(sizeof(redis_key_t));
75     if (!k) {
76         LM_ERR("Failed to allocate memory for key list entry\n");
77         goto err;
78     }
79     k->next = NULL;
80
81     k->key.s = (char*)pkg_malloc((len+1) * sizeof(char));
82     if (!k->key.s) {
83         LM_ERR("Failed to allocate memory for key list entry\n");
84         goto err;
85     }
86
87     memset(k->key.s, 0, len+1);
88     strncpy(k->key.s, entry, len);
89     k->key.len = len;
90
91     if (!*list) {
92         *list = k;
93     } else {
94         k->next = *list;
95         *list = k;
96     }
97
98     return 0;
99
100 err:
101     if (k)
102         pkg_free(k);
103     return -1;
104 }
105
106 redis_key_t * db_redis_key_shift(redis_key_t **list) {
107     redis_key_t *k;
108
109     k = *list;
110     *list = (*list)->next;
111     k->next = NULL;
112     return k;
113 }
114
115 void db_redis_key_free(redis_key_t **list) {
116     redis_key_t *l;
117     redis_key_t **head;
118
119     if (!list || !*list) {
120         return;
121     }
122     head = list;
123     do {
124         l = (*list)->next;
125         if ((*list)->key.s) {
126             pkg_free((*list)->key.s);
127             (*list)->key.s = NULL;
128             (*list)->key.len = 0;
129         }
130         pkg_free(*list);
131         *list = l;
132     } while (l);
133     *head = NULL;
134 }
135
136 int db_redis_key_list2arr(redis_key_t *list, char ***arr) {
137     int len = 0, i = 0;
138     redis_key_t *tmp = NULL;
139
140     *arr = NULL;
141     for (tmp = list, len = 0; tmp; tmp = tmp->next, len++);
142     if (len < 1) {
143         return 0;
144     }
145
146     *arr = (char**)pkg_malloc(len * sizeof (char*));
147     if (!*arr) {
148         LM_ERR("Failed to allocate memory for array\n");
149         return -1;
150     }
151     for (tmp = list, i = 0; tmp; tmp = tmp->next, i++) {
152         (*arr)[i] = tmp->key.s;
153     }
154     LM_DBG("returning %d entries\n", len);
155
156     return len;
157 }
158
159
160 int db_redis_schema_get_column_type(km_redis_con_t *con, const str *table_name, const str *col_name) {
161     struct str_hash_entry *table_e;
162     struct str_hash_entry *col_e;
163     redis_table_t *table;
164
165     table_e = str_hash_get(&con->tables, table_name->s, table_name->len);
166     if (!table_e) {
167         LM_ERR("Failed to find table '%.*s' in table hash\n",
168                 table_name->len, table_name->s);
169         return -1;
170     }
171     table = (redis_table_t*)table_e->u.p;
172     col_e = str_hash_get(&table->columns, col_name->s, col_name->len);
173     if (!col_e) {
174         LM_ERR("Failed to find column '%.*s' in schema for table '%.*s'\n",
175                 col_name->len, col_name->s,
176                 table_name->len, table_name->s);
177         return -1;
178     }
179     return col_e->u.n;
180 }
181
182 void db_redis_print_all_tables(km_redis_con_t *con) {
183     struct str_hash_table *ht;
184     struct str_hash_table *col_ht;
185     struct str_hash_entry *he;
186     struct str_hash_entry *col_he;
187     struct str_hash_entry *last;
188     struct str_hash_entry *col_last;
189     redis_table_t *table;
190     redis_key_t *key;
191     redis_type_t *type;
192     int i, j;
193
194     LM_DBG("dumping all redis tables:\n");
195     ht = &con->tables;
196
197     for (i = 0; i < ht->size; ++i) {
198         last = (&ht->table[i])->prev;
199         clist_foreach(&ht->table[i], he, next) {
200             LM_DBG("  table %.*s\n", he->key.len, he->key.s);
201             table = (redis_table_t*) he->u.p;
202
203             LM_DBG("    schema:\n");
204             col_ht = &table->columns;
205             for (j = 0; j < col_ht->size; ++j) {
206                 col_last = (&col_ht->table[j])->prev;
207                 clist_foreach(&col_ht->table[j], col_he, next) {
208                     LM_DBG("      %.*s: %d\n",
209                             col_he->key.len, col_he->key.s, col_he->u.n);
210                     if (col_he == col_last) break;
211                 }
212             }
213
214             LM_DBG("    entry keys:\n");
215             key = table->entry_keys;
216             while (key) {
217                 LM_DBG("      %.*s\n", key->key.len, key->key.s);
218                 key = key->next;
219             }
220
221             type = table->types;
222             while (type) {
223                 LM_DBG("    %.*s keys:\n", type->type.len, type->type.s);
224                 key = type->keys;
225                 while (key) {
226                     LM_DBG("      %.*s\n", key->key.len, key->key.s);
227                     key = key->next;
228                 }
229                 type = type->next;
230             }
231
232             if (he == last) break;
233         }
234     }
235 }
236
237 void db_redis_print_table(km_redis_con_t *con, char *name) {
238     str table_name;
239     struct str_hash_entry *table_entry;
240     redis_table_t *table;
241     redis_key_t *key;
242     redis_type_t *type;
243     int j;
244
245     struct str_hash_table *col_ht;
246     struct str_hash_entry *col_he;
247     struct str_hash_entry *col_last;
248
249     table_name.s = name;
250     table_name.len = strlen(name);
251
252     table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
253     if (!table_entry) {
254         LM_ERR("Failed to print table '%.*s', no such table entry found\n",
255                 table_name.len, table_name.s);
256         return;
257     }
258
259     table = (redis_table_t*) table_entry->u.p;
260     LM_DBG("table %.*s:\n", table_name.len, table_name.s);
261
262     LM_DBG("  schema:\n");
263     col_ht = &table->columns;
264     for (j = 0; j < col_ht->size; ++j) {
265         col_last = (&col_ht->table[j])->prev;
266         clist_foreach(&col_ht->table[j], col_he, next) {
267             LM_DBG("    %.*s: %d\n",
268                     col_he->key.len, col_he->key.s, col_he->u.n);
269             if (col_he == col_last) break;
270         }
271     }
272
273     LM_DBG("  entry keys:\n");
274     key = table->entry_keys;
275     while (key) {
276         LM_DBG("    %.*s\n", key->key.len, key->key.s);
277         key = key->next;
278     }
279
280     type = table->types;
281     while (type) {
282         LM_DBG("  %.*s keys:\n", type->type.len, type->type.s);
283         key = type->keys;
284         while (key) {
285             LM_DBG("    %.*s\n", key->key.len, key->key.s);
286             key = key->next;
287         }
288         type = type->next;
289     }
290 }
291
292 void db_redis_free_tables(km_redis_con_t *con) {
293     // TODO: also free schema hash?
294     struct str_hash_table *ht;
295     struct str_hash_table *col_ht;
296     struct str_hash_entry *he;
297     struct str_hash_entry *he_b;
298     struct str_hash_entry *col_he;
299     struct str_hash_entry *col_he_b;
300     struct str_hash_entry *last;
301     struct str_hash_entry *col_last;
302     redis_table_t *table;
303     redis_key_t *key, *tmpkey;
304     redis_type_t *type, *tmptype;
305     int i, j;
306
307     ht = &con->tables;
308     for (i = 0; i < ht->size; ++i) {
309         last = (&ht->table[i])->prev;
310         clist_foreach_safe(&ht->table[i], he, he_b, next) {
311             table = (redis_table_t*) he->u.p;
312
313             col_ht = &table->columns;
314             for (j = 0; j < col_ht->size; ++j) {
315                 col_last = (&col_ht->table[j])->prev;
316                 clist_foreach_safe(&col_ht->table[j], col_he, col_he_b, next) {
317                     pkg_free(col_he->key.s);
318                     if (col_he == col_last) {
319                         pkg_free(col_he);
320                         break;
321                     } else {
322                         pkg_free(col_he);
323                     }
324                 }
325             }
326             pkg_free(col_ht->table);
327
328             key = table->entry_keys;
329             while (key) {
330                 tmpkey = key;
331                 key = key->next;
332                 pkg_free(tmpkey);
333             }
334
335             type = table->types;
336             while (type) {
337                 key = type->keys;
338                 while (key) {
339                     tmpkey = key;
340                     key = key->next;
341                     pkg_free(tmpkey);
342                 }
343                 tmptype = type;
344                 type = type->next;
345                 pkg_free(tmptype);
346             }
347             pkg_free(table);
348             pkg_free(he->key.s);
349             if (he == last) {
350                 pkg_free(he);
351                 break;
352             } else {
353                 pkg_free(he);
354             }
355
356         }
357     }
358     pkg_free(ht->table);
359 }
360
361 static redis_key_t* db_redis_create_key(str *key) {
362     redis_key_t *e;
363     e = (redis_key_t*) pkg_malloc(sizeof(redis_key_t));
364     if (!e) {
365         LM_ERR("Failed to allocate memory for key entry\n");
366         return NULL;
367     }
368     memset(e, 0, sizeof(redis_key_t));
369     e->key.s = key->s;
370     e->key.len = key->len;
371     return e;
372 }
373
374 static redis_type_t* db_redis_create_type(str *type) {
375     redis_type_t *e;
376     e = (redis_type_t*) pkg_malloc(sizeof(redis_type_t));
377     if (!e) {
378         LM_ERR("Failed to allocate memory for table type\n");
379         return NULL;
380     }
381     e->type.s = type->s;
382     e->type.len = type->len;
383     e->next = NULL;
384     e->keys = NULL;
385     return e;
386 }
387
388 static struct str_hash_entry* db_redis_create_table(str *table) {
389     struct str_hash_entry *e;
390     redis_table_t *t;
391
392     LM_DBG("creating schema hash entry for table '%.*s'", table->len, table->s);
393
394     e = (struct str_hash_entry*) pkg_malloc(sizeof(struct str_hash_entry));
395     if (!e) {
396         LM_ERR("Failed to allocate memory for table entry\n");
397         return NULL;
398     }
399     memset(e, 0, sizeof(struct str_hash_entry));
400
401     if (pkg_str_dup(&e->key, table) != 0) {
402         LM_ERR("Failed to allocate memory for table name\n");
403         pkg_free(e);
404         return NULL;
405     }
406     e->flags = 0;
407
408     t = (redis_table_t*) pkg_malloc(sizeof(redis_table_t));
409     if (!t) {
410         LM_ERR("Failed to allocate memory for table data\n");
411         pkg_free(e->key.s);
412         pkg_free(e);
413         return NULL;
414     }
415     t->entry_keys = NULL;
416     t->types = NULL;
417
418     if (str_hash_alloc(&t->columns, REDIS_HT_SIZE) != 0) {
419         LM_ERR("Failed to allocate memory for table schema hashtable\n");
420         pkg_free(e->key.s);
421         pkg_free(e);
422         pkg_free(t);
423         return NULL;
424     }
425     str_hash_init(&t->columns);
426
427     e->u.p = t;
428     return e;
429 }
430
431 static struct str_hash_entry* db_redis_create_column(str *col, str *type) {
432     struct str_hash_entry *e;
433     e = (struct str_hash_entry*) pkg_malloc(sizeof(struct str_hash_entry));
434     if (!e) {
435         LM_ERR("Failed to allocate memory for column entry\n");
436         return NULL;
437     }
438     if (pkg_str_dup(&e->key, col) != 0) {
439         LM_ERR("Failed to allocate memory for column name\n");
440         pkg_free(e);
441         return NULL;
442     }
443     e->flags = 0;
444     switch (type->s[0]) {
445         case 's':
446         case 'S':
447             e->u.n = DB1_STRING;
448             break;
449         case 'i':
450         case 'I':
451             e->u.n = DB1_INT;
452             break;
453         case 'u':
454         case 'U':
455                         /* uint and ubigint */
456                         if(type->len>1 && (type->s[1]=='b' || type->s[1]=='B')) {
457                                 e->u.n = DB1_UBIGINT;
458                         } else {
459                                 e->u.n = DB1_UINT;
460                         }
461             break;
462         case 't':
463         case 'T':
464             e->u.n = DB1_DATETIME;
465             break;
466         case 'd':
467         case 'D':
468             e->u.n = DB1_DOUBLE;
469             break;
470         case 'b':
471         case 'B':
472                         /* blob and bigint */
473                         if(type->len>1 && (type->s[1]=='i' || type->s[1]=='I')) {
474                                 e->u.n = DB1_BIGINT;
475                         } else {
476                                 e->u.n = DB1_BLOB;
477                         }
478             break;
479         default:
480             LM_ERR("Invalid schema column type '%.*s', expecting one of string, int, timestamp, double, blob\n",
481                     type->len, type->s);
482             pkg_free(e->key.s);
483             pkg_free(e);
484             return NULL;
485     }
486     return e;
487 }
488
489 int db_redis_parse_keys(km_redis_con_t *con) {
490     char *p, *q;
491     char *start;
492     char *end;
493
494     str table_name = str_init("");
495     str type_name;
496     str column_name;
497     str version_code;
498
499     struct str_hash_entry *table_entry = NULL;
500     redis_table_t *table = NULL;
501     redis_type_t *type = NULL;
502     redis_type_t *type_target = NULL;
503     redis_key_t *key = NULL;
504     redis_key_t **key_target = NULL;
505     redis_key_t *key_location = NULL;
506
507     enum {
508         DBREDIS_KEYS_TABLE_ST,
509         DBREDIS_KEYS_TYPE_ST,
510         DBREDIS_KEYS_COLUMN_ST,
511         DBREDIS_KEYS_END_ST
512     } state;
513
514     if (!redis_keys.len) {
515         LM_ERR("Failed to parse empty 'keys' mod-param, please define it!\n");
516         return -1;
517     }
518
519     type_target = NULL;
520     key_location = NULL;
521     end = redis_keys.s + redis_keys.len;
522     p = start = redis_keys.s;
523     state = DBREDIS_KEYS_TABLE_ST;
524     do {
525         type = NULL;
526         key = NULL;
527         switch(state) {
528             case DBREDIS_KEYS_TABLE_ST:
529                 while(p != end && *p != '=')
530                     ++p;
531                 if (p == end) {
532                     LM_ERR("Invalid table definition, expecting <table>=<definition>\n");
533                     goto err;
534                 }
535                 table_name.s = start;
536                 table_name.len = p - start;
537
538                 version_code = (str){"",0};
539                 q = memchr(table_name.s, ':', table_name.len);
540                 if (q) {
541                     version_code = table_name;
542                     version_code.len = q - table_name.s + 1;
543                     table_name.s = q + 1;
544                     table_name.len -= version_code.len;
545                 }
546
547                 state = DBREDIS_KEYS_TYPE_ST;
548                 start = ++p;
549                 LM_DBG("found table name '%.*s'\n", table_name.len, table_name.s);
550
551                 table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
552                 if (!table_entry) {
553                     LM_ERR("No table schema found for table '%.*s', fix config"
554                                 " by adding one to the 'schema' mod-param!\n",
555                             table_name.len, table_name.s);
556                     goto err;
557                 }
558                 table = table_entry->u.p;
559                 table->version_code = version_code;
560                 break;
561             case DBREDIS_KEYS_TYPE_ST:
562                 if(!table) {
563                         LM_ERR("invalid definition, table not set\n");
564                         goto err;
565                                 }
566                 while(p != end && *p != ':')
567                     ++p;
568                 if (p == end) {
569                     LM_ERR("Invalid type definition, expecting <type>:<definition>\n");
570                     goto err;
571                 }
572                 type_name.s = start;
573                 type_name.len = p - start;
574                 state = DBREDIS_KEYS_COLUMN_ST;
575                 start = ++p;
576                 LM_DBG("found type name '%.*s' for table '%.*s'\n",
577                         type_name.len, type_name.s,
578                         table_name.len, table_name.s);
579                 if (type_name.len == REDIS_DIRECT_PREFIX_LEN &&
580                         !strncmp(type_name.s, REDIS_DIRECT_PREFIX, type_name.len)) {
581                     key_target = &table->entry_keys;
582                 } else {
583                     type = db_redis_create_type(&type_name);
584                     if (!type) goto err;
585                     if (!table->types) {
586                         table->types = type_target = type;
587                     } else {
588                         if (!type_target) {
589                             LM_ERR("Internal error accessing null type_target\n");
590                             goto err;
591                         }
592                         type_target->next = type;
593                         type_target = type_target->next;
594                     }
595                     key_target = &type->keys;
596                 }
597                 break;
598             case DBREDIS_KEYS_COLUMN_ST:
599                 while(p != end && *p != ',' && *p != '&' && *p != ';')
600                     ++p;
601                 if (p == end) {
602                     state = DBREDIS_KEYS_END_ST;
603                 } else if (*p == ',') {
604                     state = DBREDIS_KEYS_COLUMN_ST;
605                 } else if (*p == '&') {
606                     state = DBREDIS_KEYS_TYPE_ST;
607                 } else if (*p == ';') {
608                     state = DBREDIS_KEYS_TABLE_ST;
609                 }
610                 column_name.s = start;
611                 column_name.len = p - start;
612                 start = ++p;
613
614                 if (!column_name.len)
615                     break;
616
617                 /*
618                 LM_DBG("found column name '%.*s' in type '%.*s' for table '%.*s'\n",
619                         column_name.len, column_name.s,
620                         type_name.len, type_name.s,
621                         table_name.len, table_name.s);
622                 */
623                 key = db_redis_create_key(&column_name);
624                 if (!key) goto err;
625                 if (*key_target == NULL) {
626                     *key_target = key_location = key;
627                 } else {
628                     if (!key_location) {
629                         LM_ERR("Internal error, null key_location pointer\n");
630                         goto err;
631                     }
632                     key_location->next = key;
633                     key_location = key_location->next;
634                 }
635                 break;
636             case DBREDIS_KEYS_END_ST:
637                 LM_DBG("done parsing keys definition\n");
638                 return 0;
639         }
640     } while (p != end);
641
642     return 0;
643
644 err:
645     if (type)
646         pkg_free(type);
647     if (key)
648         pkg_free(key);
649     db_redis_free_tables(con);
650     return -1;
651 }
652
653
654 int db_redis_parse_schema(km_redis_con_t *con) {
655     DIR *srcdir;
656     FILE *fin;
657     struct dirent* dent;
658     char *dir_name;
659
660     str table_name = str_init("");
661     str column_name;
662     str type_name;
663
664     struct str_hash_entry *table_entry = NULL;
665     struct str_hash_entry *column_entry = NULL;
666     redis_table_t *table = NULL;
667
668     char full_path[_POSIX_PATH_MAX + 1];
669     int path_len;
670     struct stat fstat;
671     unsigned char c;
672     int cc;
673
674     enum {
675         DBREDIS_SCHEMA_COLUMN_ST,
676         DBREDIS_SCHEMA_TYPE_ST,
677         DBREDIS_SCHEMA_VERSION_ST,
678         DBREDIS_SCHEMA_END_ST,
679     } state;
680
681     char buf[4096];
682     char *bufptr;
683
684
685     srcdir = NULL;
686     fin = NULL;
687     dir_name = NULL;
688
689     //LM_DBG("parsing schema '%.*s'\n", redis_schema.len, redis_schema.s);
690     if (!redis_schema_path.len) {
691         LM_ERR("Failed to parse empty 'schema_path' mod-param, please define it!\n");
692         return -1;
693     }
694
695     dir_name = (char*)pkg_malloc((redis_schema_path.len + 1) * sizeof(char));
696     if (!dir_name) {
697         LM_ERR("Failed to allocate memory for schema directory name\n");
698         goto err;
699     }
700     strncpy(dir_name, redis_schema_path.s, redis_schema_path.len);
701     dir_name[redis_schema_path.len] = '\0';
702     srcdir = opendir(dir_name);
703     if (!srcdir) {
704         LM_ERR("Failed to open schema directory '%s'\n", dir_name);
705         goto err;
706     }
707
708     if (str_hash_alloc(&con->tables, REDIS_HT_SIZE) != 0) {
709         LM_ERR("Failed to allocate memory for tables hashtable\n");
710         goto err;
711     }
712     str_hash_init(&con->tables);
713
714     while((dent = readdir(srcdir)) != NULL) {
715         path_len = redis_schema_path.len;
716         if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
717             continue;
718         }
719         if ((path_len + strlen(dent->d_name) + 1) > _POSIX_PATH_MAX) {
720             LM_WARN("Redis schema path '%.*s/%s' is too long, skipping...\n",
721                 redis_schema_path.len, redis_schema_path.s, dent->d_name);
722             continue;
723         }
724         snprintf(full_path, sizeof(full_path), "%s/%s", dir_name, dent->d_name);
725
726         if (stat(full_path, &fstat) < 0) {
727             LM_ERR("Failed to stat schema file %s\n", full_path);
728             continue;
729         }
730         if (!S_ISREG(fstat.st_mode)) {
731             LM_DBG("skipping schema file '%s' as it's not a regular file\n", full_path);
732             continue;
733         }
734
735         LM_DBG("reading schema full path '%s'\n", full_path);
736
737         fin = fopen(full_path, "r");
738         if (!fin) {
739             LM_ERR("Failed to open redis schema file '%s'\n", full_path);
740             continue;
741         }
742
743         table_name.s = dent->d_name;
744         table_name.len = strlen(table_name.s);
745         table_entry = str_hash_get(&con->tables, table_name.s, table_name.len);
746         if (table_entry) {
747             // should not happen, as this would require two files with same name
748             LM_WARN("Found duplicate table schema definition '%.*s', skipping...\n",
749                     table_name.len, table_name.s);
750             fclose(fin);
751             continue;
752         }
753         table_entry = db_redis_create_table(&table_name);
754         if (!table_entry) goto err;
755         str_hash_add(&con->tables, table_entry);
756         table = table_entry->u.p;
757
758         state = DBREDIS_SCHEMA_COLUMN_ST;
759         memset(buf, 0, sizeof(buf));
760         bufptr = buf;
761         do {
762             if (bufptr - buf > sizeof(buf)) {
763                 LM_ERR("Schema line too long in file %s\n", full_path);
764                 goto err;
765             }
766
767             cc = fgetc(fin);
768             c = (unsigned char)cc;
769
770             if (c == '\r')
771                 continue;
772             //LM_DBG("parsing char %c, buf is '%s' at pos %lu\n", c, buf, bufpos);
773             switch(state) {
774                 case DBREDIS_SCHEMA_COLUMN_ST:
775                     if (cc == EOF) {
776                         LM_ERR("Unexpected end of file in schema column name of file %s\n", full_path);
777                         goto err;
778                     }
779                     if(c != '\n' && c != '/') {
780                         *bufptr = c;
781                         bufptr++;
782                         continue;
783                     }
784                     if (c == '\n') {
785                         if (bufptr == buf) {
786                             // trailing comma, skip
787                             state = DBREDIS_SCHEMA_VERSION_ST;
788                             continue;
789                         } else {
790                             LM_ERR("Invalid column definition, expecting <column>/<type>\n");
791                             goto err;
792                         }
793                     }
794                     column_name.s = buf;
795                     column_name.len = bufptr - buf;
796                     bufptr++;
797                     state = DBREDIS_SCHEMA_TYPE_ST;
798                     LM_DBG("found column name '%.*s'\n", column_name.len, column_name.s);
799                     break;
800                 case DBREDIS_SCHEMA_TYPE_ST:
801                     if (cc == EOF) {
802                         LM_ERR("Unexpected end of file in schema column type of file %s\n", full_path);
803                         goto err;
804                     }
805                     if(c != '\n' && c != ',') {
806                         *bufptr = c;
807                         bufptr++;
808                         continue;
809                     }
810                     type_name.s = buf + column_name.len + 1;
811                     type_name.len = bufptr - type_name.s;
812
813                     if (c == '\n') {
814                         state = DBREDIS_SCHEMA_VERSION_ST;
815                     } else {
816                         state = DBREDIS_SCHEMA_COLUMN_ST;
817                     }
818                     /*
819                     LM_DBG("found column type '%.*s' with len %d for column name '%.*s' in table '%.*s'\n",
820                             type_name.len, type_name.s,
821                             type_name.len,
822                             column_name.len, column_name.s,
823                             table_name.len, table_name.s);
824                     */
825                     column_entry = str_hash_get(&table->columns, column_name.s, column_name.len);
826                     if (column_entry) {
827                         LM_ERR("Found duplicate column definition '%.*s' in schema definition of table '%.*s', remove one from schema!\n",
828                                 column_name.len, column_name.s,
829                                 table_name.len, table_name.s);
830                         goto err;
831                     }
832                     column_entry = db_redis_create_column(&column_name, &type_name);
833                     if (!column_entry) {
834                         goto err;
835                     }
836                     str_hash_add(&table->columns, column_entry);
837                     memset(buf, 0, sizeof(buf));
838                     bufptr = buf;
839                     break;
840                 case DBREDIS_SCHEMA_VERSION_ST:
841                     if (c != '\n' && cc != EOF) {
842                         *bufptr = c;
843                         bufptr++;
844                         continue;
845                     }
846                     *bufptr = '\0';
847                     table->version = atoi(buf);
848                     state = DBREDIS_SCHEMA_END_ST;
849                     break;
850                 case DBREDIS_SCHEMA_END_ST:
851                     goto fileend;
852                     break;
853             }
854         } while (cc != EOF);
855
856 fileend:
857         fclose(fin);
858         fin = NULL;
859     }
860
861
862     closedir(srcdir);
863     pkg_free(dir_name);
864
865     return 0;
866 err:
867     if (fin)
868         fclose(fin);
869     if (srcdir)
870         closedir(srcdir);
871     if (dir_name)
872         pkg_free(dir_name);
873
874     db_redis_free_tables(con);
875     return -1;
876 }
877
878 int db_redis_keys_spec(char *spec) {
879     size_t len = strlen(spec);
880
881     if (redis_keys.len == 0) {
882         redis_keys.s = (char*)pkg_malloc(len * sizeof(char));
883         if (!redis_keys.s) {
884             LM_ERR("Failed to allocate memory for keys spec\n");
885             goto err;
886         }
887     } else {
888         redis_keys.s = (char*)pkg_realloc(redis_keys.s, redis_keys.len + 1 + len);
889         if (!redis_keys.s) {
890             LM_ERR("Failed to reallocate memory for keys spec\n");
891             goto err;
892         }
893         redis_keys.s[redis_keys.len] = ';';
894         redis_keys.len++;
895     }
896
897     strncpy(redis_keys.s + redis_keys.len, spec, len);
898     redis_keys.len += len;
899
900     return 0;
901
902 err:
903     if (redis_keys.len) {
904         pkg_free(redis_keys.s);
905     }
906     return -1;
907 }