<p>Dennis has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14698">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">cel_odbc: Rewritten most of cel_odbc<br><br>We found that if a datasource is unavailable when cel_odbc.conf is<br>processed, the table is skipped and no records will ever be inserted<br>into the skipped table.<br><br>While spliting code apart, I found lots of interesting problems hidden<br>inside this file and ended up rewriting most of it. Trying to keep<br>changes minimal became a near impossible task because of the amount of<br>collisions and rewrites.<br><br>The first problem I encountered was malloc tricks and pointer hacks,<br>stuffing null-terminated strings below structures. As these structures<br>were only created when cel_odbc.conf is parsed, the loss of<br>maintainability far outweighs any performance gained from doing so.<br><br>Next, I split up all the massive functions best I could. Each function<br>now has its own exact purpose. Some functions were renamed during<br>this process to better describe what they do.<br><br>I also renamed many variables to what they really are/contained.<br>Redundant logging of allocation failures was removed as ast_calloc<br>already logs them. Functions were added to allocate and initialize<br>every structure with proper default values. Lots of temporary variables<br>were removed, avoiding extra string copies.<br><br>Arbitrary length limits on strings have been mostly removed.<br>Configuration file is now traversed instead of scanned repeatedly.<br><br>The SQL_ERROR return code from SQLFetch is now handled properly,<br>causing the table to be skipped instead of trying to perform partial<br>inserts into it.<br><br>Optimizing odbc_log() was fairly interesting and while I haven't<br>benchmarked should be fairly fruitful. The original algorithm<br>constructed completely new SQL for every table on every event,<br>stringifying values in the process and performing<br>events*tables*columns*20-ish string comparisons. The new algorithm<br>generates static queries once tables columns are known. Relative<br>addresses are generated for all parameters, to be used later when<br>binding parameters. By binding parameters instead of stringifying them<br>we allow the driver to decide on the most optimal conversion.<br><br>All identifiers have been wrapped with backticks, which should close<br>any tickets related to column names and reserved words. Because we no<br>longer perform type checks on all columns, I was able to discard lots of<br>extra data from the columns struct.<br><br>Lastly, I tried to make verbose logs a bit more clear and useful.<br><br>Hopefully I was able to re-implement the "static", "filter" and "alias"<br>features properly. Documentation on how they actually function was<br>somewhat light.<br><br>This module now works as follows:<br><br>First, it reads the configuration file and stores all relevant bits in<br>structures for later. Once this is done, it will attempt to retrieve<br>column names for every table mentioned in the config file, construct a<br>query for these tables and pre-bind any parameters.<br><br>For any tables that were inaccessible at config time, a manager thread<br>will go through the list of tables every 10 seconds and attempts to<br>construct queries for any tables that this module failed to connect to<br>previously. Once all tables have a valid query, the manager thread will<br>self-terminate.<br><br>ASTERISK-29012 #close<br><br>Change-Id: Id85d81add33096f8282d212daf239f2fc845d783<br>---<br>M cel/cel_odbc.c<br>1 file changed, 815 insertions(+), 680 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/98/14698/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/cel/cel_odbc.c b/cel/cel_odbc.c</span><br><span>index f6b6321..e85ea57 100644</span><br><span>--- a/cel/cel_odbc.c</span><br><span>+++ b/cel/cel_odbc.c</span><br><span>@@ -7,6 +7,9 @@</span><br><span>  * Tilghman Lesher <tlesher AT digium DOT com></span><br><span>  * by Steve Murphy</span><br><span>  *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Refactored most of this module to support delayed connectivity.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Dennis Buteyn <dennis.buteyn@xorcom.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span>  * See http://www.asterisk.org for more information about</span><br><span>  * the Asterisk project. Please do not directly contact</span><br><span>  * any of the maintainers of this project for assistance;</span><br><span>@@ -51,6 +54,7 @@</span><br><span> #include "asterisk/res_odbc.h"</span><br><span> #include "asterisk/cel.h"</span><br><span> #include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/sem.h"</span><br><span> </span><br><span> #define        CONFIG  "cel_odbc.conf"</span><br><span> </span><br><span>@@ -62,49 +66,602 @@</span><br><span> /*! TRUE if we should set the eventtype field to USER_DEFINED on user events. */</span><br><span> static unsigned char cel_show_user_def;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* Optimization to reduce number of memory allocations */</span><br><span style="color: hsl(0, 100%, 40%);">-static int maxsize = 512, maxsize2 = 512;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> struct columns {</span><br><span style="color: hsl(0, 100%, 40%);">-   char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-     char *celname;</span><br><span style="color: hsl(0, 100%, 40%);">-  char *filtervalue;</span><br><span style="color: hsl(0, 100%, 40%);">-      char *staticvalue;</span><br><span style="color: hsl(0, 100%, 40%);">-      SQLSMALLINT type;</span><br><span style="color: hsl(0, 100%, 40%);">-       SQLINTEGER size;</span><br><span style="color: hsl(0, 100%, 40%);">-        SQLSMALLINT decimals;</span><br><span style="color: hsl(0, 100%, 40%);">-   SQLSMALLINT radix;</span><br><span style="color: hsl(0, 100%, 40%);">-      SQLSMALLINT nullable;</span><br><span style="color: hsl(0, 100%, 40%);">-   SQLINTEGER octetlen;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(name);</span><br><span style="color: hsl(120, 100%, 40%);">+       );</span><br><span style="color: hsl(120, 100%, 40%);">+    const char * staticval; /* Reference to staticval->value, no need to free */</span><br><span style="color: hsl(120, 100%, 40%);">+       int rva; /* Relative offset into ast_cel_event_record */</span><br><span>     AST_LIST_ENTRY(columns) list;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct aliases {</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(source);</span><br><span style="color: hsl(120, 100%, 40%);">+             AST_STRING_FIELD(target);</span><br><span style="color: hsl(120, 100%, 40%);">+     );</span><br><span style="color: hsl(120, 100%, 40%);">+    int rva; /* Relative offset into ast_cel_event_record */</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_LIST_ENTRY(aliases) list;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct staticvals {</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(colname);</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(value);</span><br><span style="color: hsl(120, 100%, 40%);">+      );</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_LIST_ENTRY(staticvals) list;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct filters {</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(celname);</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(value);</span><br><span style="color: hsl(120, 100%, 40%);">+      );</span><br><span style="color: hsl(120, 100%, 40%);">+    int rva; /* Relative offset into ast_cel_event_record */</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_LIST_ENTRY(filters) list;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct tables {</span><br><span style="color: hsl(0, 100%, 40%);">-  char *connection;</span><br><span style="color: hsl(0, 100%, 40%);">-       char *table;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_DECLARE_STRING_FIELDS(</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_STRING_FIELD(name);</span><br><span style="color: hsl(120, 100%, 40%);">+               AST_STRING_FIELD(category);</span><br><span style="color: hsl(120, 100%, 40%);">+           AST_STRING_FIELD(connection);</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_STRING_FIELD(query);</span><br><span style="color: hsl(120, 100%, 40%);">+      );</span><br><span>   unsigned int usegmtime:1;</span><br><span>    unsigned int allowleapsec:1;</span><br><span>         AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_LIST_HEAD_NOLOCK(odbc_aliases, aliases) aliases;</span><br><span style="color: hsl(120, 100%, 40%);">+  AST_LIST_HEAD_NOLOCK(odbc_staticvals, staticvals) staticvals;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_HEAD_NOLOCK(odbc_filters, filters) filters;</span><br><span>         AST_RWLIST_ENTRY(tables) list;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Used when calling generic_prepare */</span><br><span style="color: hsl(120, 100%, 40%);">+struct prepare_params {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct tables *table;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_cel_event_record record;</span><br><span style="color: hsl(120, 100%, 40%);">+   SQL_TIMESTAMP_STRUCT event_time;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_sem manager_sem; /* Raised when manager told to stop */</span><br><span style="color: hsl(120, 100%, 40%);">+static pthread_t manager_thread = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct columns * create_column(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct columns *column = ast_calloc(1, sizeof(*column));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (column) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ast_string_field_init(column, 256) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        column->rva = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  return column;</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_free(column);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_column(struct columns *column)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_string_field_free_memory(column);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(column);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_columns(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     while ((column = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             destroy_column(column);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct aliases * create_alias(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct aliases *alias = ast_calloc(1, sizeof(*alias));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (alias) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ast_string_field_init(alias, 256) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 alias->rva = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                   return alias;</span><br><span style="color: hsl(120, 100%, 40%);">+         } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_free(alias);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_alias(struct aliases *alias)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_string_field_free_memory(alias);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(alias);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_aliases(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct aliases *alias;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      while ((alias = AST_RWLIST_REMOVE_HEAD(&(table->aliases), list))) {</span><br><span style="color: hsl(120, 100%, 40%);">+            destroy_alias(alias);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int append_alias(</span><br><span style="color: hsl(120, 100%, 40%);">+      struct tables *table,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *source,</span><br><span style="color: hsl(120, 100%, 40%);">+   const char *target,</span><br><span style="color: hsl(120, 100%, 40%);">+   int rva</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct aliases *alias = create_alias();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (alias) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_string_field_set(alias, source, source);</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_string_field_set(alias, target, target);</span><br><span style="color: hsl(120, 100%, 40%);">+          alias->rva = rva;</span><br><span style="color: hsl(120, 100%, 40%);">+          AST_LIST_INSERT_TAIL(&(table->aliases), alias, list);</span><br><span style="color: hsl(120, 100%, 40%);">+          return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct staticvals * create_staticval(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct staticvals *staticval = ast_calloc(1, sizeof(*staticval));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (staticval) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ast_string_field_init(staticval, 256) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     return staticval;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_free(staticval);</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_staticval(struct staticvals *staticval)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_string_field_free_memory(staticval);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_free(staticval);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_staticvals(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct staticvals *staticval;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       while ((staticval = AST_RWLIST_REMOVE_HEAD(&(table->staticvals), list))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             destroy_staticval(staticval);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int append_staticval(</span><br><span style="color: hsl(120, 100%, 40%);">+  struct tables *table,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *colname,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *value</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct staticvals *staticval = create_staticval();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (staticval) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_string_field_set(staticval, colname, colname);</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_string_field_set(staticval, value, value);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_LIST_INSERT_TAIL(&(table->staticvals), staticval, list);</span><br><span style="color: hsl(120, 100%, 40%);">+           return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct filters * create_filter(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct filters *filter = ast_calloc(1, sizeof(*filter));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (filter) {</span><br><span style="color: hsl(120, 100%, 40%);">+         if (ast_string_field_init(filter, 256) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        filter->rva = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  return filter;</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_free(filter);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_filter(struct filters *filter)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_string_field_free_memory(filter);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(filter);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_filters(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct filters *filter;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     while ((filter = AST_RWLIST_REMOVE_HEAD(&(table->filters), list))) {</span><br><span style="color: hsl(120, 100%, 40%);">+           destroy_filter(filter);</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int append_filter(</span><br><span style="color: hsl(120, 100%, 40%);">+     struct tables *table,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *celname,</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *value,</span><br><span style="color: hsl(120, 100%, 40%);">+    int rva</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct filters *filter = create_filter();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (filter) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_string_field_set(filter, celname, celname);</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_string_field_set(filter, value, value);</span><br><span style="color: hsl(120, 100%, 40%);">+           filter->rva = rva;</span><br><span style="color: hsl(120, 100%, 40%);">+         AST_LIST_INSERT_TAIL(&(table->filters), filter, list);</span><br><span style="color: hsl(120, 100%, 40%);">+         return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct tables * create_table(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct tables *table = ast_calloc(1, sizeof(*table));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (table) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ast_string_field_init(table, 256) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 table->usegmtime = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      table->allowleapsec = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_string_field_set(table, name, "cel");</span><br><span style="color: hsl(120, 100%, 40%);">+                   return table;</span><br><span style="color: hsl(120, 100%, 40%);">+         } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_free(table);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_table(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ destroy_columns(table);</span><br><span style="color: hsl(120, 100%, 40%);">+       destroy_aliases(table);</span><br><span style="color: hsl(120, 100%, 40%);">+       destroy_staticvals(table);</span><br><span style="color: hsl(120, 100%, 40%);">+    destroy_filters(table);</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_string_field_free_memory(table);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_free(table);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_tables(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct tables *table;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {</span><br><span style="color: hsl(120, 100%, 40%);">+            destroy_table(table);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void destroy_tables_safely(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    while (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Unable to lock table list for modification. Retrying.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     destroy_tables();</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int wait_for_signal(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct timeval timeout_val = ast_tvadd(ast_tvnow(), ast_tv(10, 0));</span><br><span style="color: hsl(120, 100%, 40%);">+   struct timespec timeout_spec = {</span><br><span style="color: hsl(120, 100%, 40%);">+              .tv_sec = timeout_val.tv_sec,</span><br><span style="color: hsl(120, 100%, 40%);">+         .tv_nsec = timeout_val.tv_usec * 1000,</span><br><span style="color: hsl(120, 100%, 40%);">+        };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_sem_timedwait(&manager_sem, &timeout_spec);</span><br><span style="color: hsl(120, 100%, 40%);">+       return errno;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int cel_record_rva(const char * name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strings_equal(name, "eventtime")) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return offsetof(struct ast_cel_event_record, event_time);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (ast_strings_equal(name, "userdeftype")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return offsetof(struct ast_cel_event_record, user_defined_name);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (ast_strings_equal(name, "cid_name")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, caller_id_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_strings_equal(name, "cid_num")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return offsetof(struct ast_cel_event_record, caller_id_num);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ast_strings_equal(name, "cid_ani")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return offsetof(struct ast_cel_event_record, caller_id_ani);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ast_strings_equal(name, "cid_rdnis")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return offsetof(struct ast_cel_event_record, caller_id_rdnis);</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (ast_strings_equal(name, "cid_dnid")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, caller_id_dnid);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_strings_equal(name, "exten")) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return offsetof(struct ast_cel_event_record, extension);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (ast_strings_equal(name, "context")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return offsetof(struct ast_cel_event_record, context);</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (ast_strings_equal(name, "channame")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, channel_name);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (ast_strings_equal(name, "appname")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return offsetof(struct ast_cel_event_record, application_name);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (ast_strings_equal(name, "appdata")) {</span><br><span style="color: hsl(120, 100%, 40%);">+            return offsetof(struct ast_cel_event_record, application_data);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else if (ast_strings_equal(name, "accountcode")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return offsetof(struct ast_cel_event_record, account_code);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (ast_strings_equal(name, "peeraccount")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return offsetof(struct ast_cel_event_record, peer_account);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (ast_strings_equal(name, "uniqueid")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, unique_id);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (ast_strings_equal(name, "linkedid")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, linked_id);</span><br><span style="color: hsl(120, 100%, 40%);">+      } else if (ast_strings_equal(name, "userfield")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return offsetof(struct ast_cel_event_record, user_field);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (ast_strings_equal(name, "peer")) {</span><br><span style="color: hsl(120, 100%, 40%);">+               return offsetof(struct ast_cel_event_record, peer);</span><br><span style="color: hsl(120, 100%, 40%);">+   } else if (ast_strings_equal(name, "amaflags")) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return offsetof(struct ast_cel_event_record, amaflag);</span><br><span style="color: hsl(120, 100%, 40%);">+        } else if (ast_strings_equal(name, "extra")) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return offsetof(struct ast_cel_event_record, extra);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (ast_strings_equal(name, "eventtype")) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return offsetof(struct ast_cel_event_record, event_type);</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void construct_query(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_str *query = ast_str_create(2000);</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0, i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_str_append(&query, 0, "INSERT INTO `%s` (", table->name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_LIST_TRAVERSE(&(table->columns), column, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (column->rva >= 0 || column->staticval) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (count > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_str_append(&query, 0, ", `%s`", column->name);</span><br><span style="color: hsl(120, 100%, 40%);">+                   } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              ast_str_append(&query, 0, "`%s`", column->name);</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     count++;</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Cannot construct query with zero values to insert. */</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_free(query);</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_str_append(&query, 0, "%s", ") VALUES (");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < count; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (i > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ast_str_append(&query, 0, "%s", ", ?");</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_str_append(&query, 0, "%s", "?");</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_str_append(&query, 0, "%s", ")");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_string_field_set(table, query, ast_str_buffer(query));</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_free(query);</span><br><span style="color: hsl(120, 100%, 40%);">+      ast_verb(3, "Constructed query for %s@%s: %s\n", table->name, table->connection, table->query);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int fetch_table_columns(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct odbc_obj *obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ SQLHSTMT stmt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+       char columnname[80];</span><br><span style="color: hsl(120, 100%, 40%);">+  int res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    obj = ast_odbc_request_obj(table->connection, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!obj) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_WARNING, "Connection '%s' unavailable at this time.\n", table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_odbc_release_obj(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   res = SQLColumns(stmt, NULL, 0, NULL, 0, (SQLCHAR*) table->name, SQL_NTS, NULL, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+         SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_odbc_release_obj(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+           res = SQLFetch(stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               if (res == SQL_NO_DATA) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* No more columns, all done */</span><br><span style="color: hsl(120, 100%, 40%);">+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_odbc_release_obj(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Got a column, save it */</span><br><span style="color: hsl(120, 100%, 40%);">+                   column = create_column();</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (column) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_string_field_set(column, name, columnname);</span><br><span style="color: hsl(120, 100%, 40%);">+                               column->rva = cel_record_rva(columnname);</span><br><span style="color: hsl(120, 100%, 40%);">+                          AST_LIST_INSERT_TAIL(&(table->columns), column, list);</span><br><span style="color: hsl(120, 100%, 40%);">+                 } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Out of memory, bail out */</span><br><span style="color: hsl(120, 100%, 40%);">+                         destroy_columns(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_odbc_release_obj(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+                            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Something bad happened, bail out */</span><br><span style="color: hsl(120, 100%, 40%);">+                        destroy_columns(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_odbc_release_obj(obj);</span><br><span style="color: hsl(120, 100%, 40%);">+                    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void load_general_config(struct ast_config *cfg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct ast_variable *var;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   cel_show_user_def = CEL_SHOW_USERDEF_DEFAULT;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (strcasecmp(var->name, "show_user_defined") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   cel_show_user_def = ast_true(var->value) ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char * strip_quotes(char *input)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Only to be used with stack-allocated memory! */</span><br><span style="color: hsl(120, 100%, 40%);">+    if (input[0] == '"' && input[strlen(input) - 1] == '"') {</span><br><span style="color: hsl(120, 100%, 40%);">+           input[strlen(input) - 1] = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+              return input + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return input;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_table_config(</span><br><span style="color: hsl(120, 100%, 40%);">+     struct ast_config *cfg,</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *category</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+     struct tables *table;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *var;</span><br><span style="color: hsl(120, 100%, 40%);">+     int rva;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    table = create_table();</span><br><span style="color: hsl(120, 100%, 40%);">+       if (table == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_string_field_set(table, category, category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (var = ast_variable_browse(cfg, category); var; var = var->next) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (strcasecmp(var->name, "connection") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_string_field_set(table, connection, var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+               } else if (strcasecmp(var->name, "table") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_string_field_set(table, name, var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (strcasecmp(var->name, "usegmtime") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    table->usegmtime = ast_true(var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+                } else if (strcasecmp(var->name, "allowleapsecond") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      table->allowleapsec = ast_true(var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (strncmp(var->name, "alias", 5) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        char *source = ast_strip(ast_strdupa(var->name + 5));</span><br><span style="color: hsl(120, 100%, 40%);">+                      rva = cel_record_rva(source);</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (rva >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (!append_alias(table, source, var->value, rva)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                       destroy_table(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                                 return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (strncmp(var->name, "static", 6) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       char *value = strip_quotes(ast_strip(ast_strdupa(var->name + 6)));</span><br><span style="color: hsl(120, 100%, 40%);">+                 if (!append_staticval(table, var->value, value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         destroy_table(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (strncmp(var->name, "filter", 6) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       char *celname = ast_strip(ast_strdupa(var->name + 6));</span><br><span style="color: hsl(120, 100%, 40%);">+                     rva = cel_record_rva(celname);</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (rva >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (append_filter(table, celname, var->value, rva)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      ast_verb(3, "Filtering CEL events matching %s = \"%s\" in %s@%s\n", celname, var->value, table->name, table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+                                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      destroy_table(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                                 return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_strlen_zero(table->connection)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", category);</span><br><span style="color: hsl(120, 100%, 40%);">+              destroy_table(table);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_verb(3, "Found CEL table %s@%s.\n", table->name, table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+              AST_RWLIST_INSERT_TAIL(&odbc_tables, table, list);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void resolve_aliases(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct aliases *alias;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Somewhat inefficient but not like we're doing this every insertion. */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(&(table->aliases), alias, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+            AST_LIST_TRAVERSE(&(table->columns), column, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (ast_strings_equal(column->name, alias->target)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           column->rva = alias->rva;</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_verb(3, "Aliasing %s to %s in %s@%s\n", alias->source, alias->target, table->name, table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+                              break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void resolve_staticvals(struct tables *table)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct staticvals *staticval;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Somewhat inefficient but not like we're doing this every insertion. */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(&(table->staticvals), staticval, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             AST_LIST_TRAVERSE(&(table->columns), column, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (ast_strings_equal(column->name, staticval->colname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                              column->staticval = staticval->value;</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_verb(3, "Forcing %s to \"%s\" in %s@%s\n", staticval->colname, staticval->value, table->name, table->connection);</span><br><span style="color: hsl(120, 100%, 40%);">+                         break;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int construct_queries(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tables *table;</span><br><span style="color: hsl(120, 100%, 40%);">+ int failures = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   AST_LIST_TRAVERSE(&odbc_tables, table, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!ast_strlen_zero(table->query)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (fetch_table_columns(table)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      resolve_aliases(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                       resolve_staticvals(table);</span><br><span style="color: hsl(120, 100%, 40%);">+                    construct_query(table);</span><br><span style="color: hsl(120, 100%, 40%);">+               } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      failures += 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return failures;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> static int load_config(void)</span><br><span> {</span><br><span>         struct ast_config *cfg;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_variable *var;</span><br><span style="color: hsl(0, 100%, 40%);">-       const char *tmp, *catg;</span><br><span style="color: hsl(0, 100%, 40%);">- struct tables *tableptr;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct columns *entry;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct odbc_obj *obj;</span><br><span style="color: hsl(0, 100%, 40%);">-   char columnname[80];</span><br><span style="color: hsl(0, 100%, 40%);">-    char connection[40];</span><br><span style="color: hsl(0, 100%, 40%);">-    char table[40];</span><br><span style="color: hsl(0, 100%, 40%);">- int lenconnection, lentable;</span><br><span style="color: hsl(0, 100%, 40%);">-    SQLLEN sqlptr;</span><br><span style="color: hsl(0, 100%, 40%);">-  int res = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-    SQLHSTMT stmt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *catg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */</span><br><span> </span><br><span>  cfg = ast_config_load(CONFIG, config_flags);</span><br><span>@@ -113,211 +670,149 @@</span><br><span>               return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Process the general category */</span><br><span style="color: hsl(0, 100%, 40%);">-      cel_show_user_def = CEL_SHOW_USERDEF_DEFAULT;</span><br><span style="color: hsl(0, 100%, 40%);">-   for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {</span><br><span style="color: hsl(0, 100%, 40%);">-            if (!strcasecmp(var->name, "show_user_defined")) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 cel_show_user_def = ast_true(var->value) ? 1 : 0;</span><br><span style="color: hsl(0, 100%, 40%);">-            } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* Unknown option name. */</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              if (!strcasecmp(catg, "general")) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (strcasecmp(catg, "general") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     load_general_config(cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (!load_table_config(cfg, catg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   destroy_tables();</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_config_destroy(cfg);</span><br><span style="color: hsl(120, 100%, 40%);">+                      return -1;</span><br><span>           }</span><br><span style="color: hsl(0, 100%, 40%);">-               var = ast_variable_browse(cfg, catg);</span><br><span style="color: hsl(0, 100%, 40%);">-           if (!var)</span><br><span style="color: hsl(0, 100%, 40%);">-                       continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", catg);</span><br><span style="color: hsl(0, 100%, 40%);">-                    continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_copy_string(connection, tmp, sizeof(connection));</span><br><span style="color: hsl(0, 100%, 40%);">-           lenconnection = strlen(connection);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-             /* When loading, we want to be sure we can connect. */</span><br><span style="color: hsl(0, 100%, 40%);">-          obj = ast_odbc_request_obj(connection, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-              if (!obj) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ".  Check res_odbc.conf.\n", connection, catg);</span><br><span style="color: hsl(0, 100%, 40%);">-                       continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_log(LOG_NOTICE, "No table name found.  Assuming 'cel'.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                       tmp = "cel";</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_copy_string(table, tmp, sizeof(table));</span><br><span style="color: hsl(0, 100%, 40%);">-             lentable = strlen(table);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);</span><br><span style="color: hsl(0, 100%, 40%);">-          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_odbc_release_obj(obj);</span><br><span style="color: hsl(0, 100%, 40%);">-                      continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);</span><br><span style="color: hsl(0, 100%, 40%);">-             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.  Skipping.\n", connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                  ast_odbc_release_obj(obj);</span><br><span style="color: hsl(0, 100%, 40%);">-                      continue;</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-              if (!tableptr) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_odbc_release_obj(obj);</span><br><span style="color: hsl(0, 100%, 40%);">-                      res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               tableptr->connection = (char *)tableptr + sizeof(*tableptr);</span><br><span style="color: hsl(0, 100%, 40%);">-         tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_copy_string(tableptr->connection, connection, lenconnection + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-                ast_copy_string(tableptr->table, table, lentable + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               tableptr->usegmtime = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-             if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  tableptr->usegmtime = ast_true(tmp);</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               tableptr->allowleapsec = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-          if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "allowleapsecond"))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    tableptr->allowleapsec = ast_true(tmp);</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_verb(3, "Found CEL table %s@%s.\n", tableptr->table, tableptr->connection);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-         /* Check for filters first */</span><br><span style="color: hsl(0, 100%, 40%);">-           for (var = ast_variable_browse(cfg, catg); var; var = var->next) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (strncmp(var->name, "filter", 6) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                char *celvar = ast_strdupa(var->name + 6);</span><br><span style="color: hsl(0, 100%, 40%);">-                           celvar = ast_strip(celvar);</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                            entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-                              if (!entry) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                   ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                                        res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                               /* NULL column entry means this isn't a column in the database */</span><br><span style="color: hsl(0, 100%, 40%);">-                           entry->name = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-                          entry->celname = (char *)entry + sizeof(*entry);</span><br><span style="color: hsl(0, 100%, 40%);">-                             entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                            strcpy(entry->celname, celvar);</span><br><span style="color: hsl(0, 100%, 40%);">-                              strcpy(entry->filtervalue, var->value);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                           AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);</span><br><span style="color: hsl(0, 100%, 40%);">-                 }</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     char *celvar = "", *staticvalue = "";</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                  /* Is there an alias for this column? */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* NOTE: This seems like a non-optimal parse method, but I'm going</span><br><span style="color: hsl(0, 100%, 40%);">-                   * for user configuration readability, rather than fast parsing. We</span><br><span style="color: hsl(0, 100%, 40%);">-                      * really don't parse this file all that often, anyway.</span><br><span style="color: hsl(0, 100%, 40%);">-                      */</span><br><span style="color: hsl(0, 100%, 40%);">-                     for (var = ast_variable_browse(cfg, catg); var; var = var->next) {</span><br><span style="color: hsl(0, 100%, 40%);">-                           if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                   char *alias = ast_strdupa(var->name + 5);</span><br><span style="color: hsl(0, 100%, 40%);">-                                    celvar = ast_strip(alias);</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                                      break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                   char *item = ast_strdupa(var->name + 6);</span><br><span style="color: hsl(0, 100%, 40%);">-                                     item = ast_strip(item);</span><br><span style="color: hsl(0, 100%, 40%);">-                                 if (item[0] == '"' && item[strlen(item) - 1] == '"') {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                /* Remove surrounding quotes */</span><br><span style="color: hsl(0, 100%, 40%);">-                                         item[strlen(item) - 1] = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-                                          item++;</span><br><span style="color: hsl(0, 100%, 40%);">-                                 }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       staticvalue = item;</span><br><span style="color: hsl(0, 100%, 40%);">-                             }</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!entry) {</span><br><span style="color: hsl(0, 100%, 40%);">-                           ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);</span><br><span style="color: hsl(0, 100%, 40%);">-                         res = -1;</span><br><span style="color: hsl(0, 100%, 40%);">-                               break;</span><br><span style="color: hsl(0, 100%, 40%);">-                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                       entry->name = (char *)entry + sizeof(*entry);</span><br><span style="color: hsl(0, 100%, 40%);">-                        strcpy(entry->name, columnname);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                     if (!ast_strlen_zero(celvar)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                         entry->celname = entry->name + strlen(columnname) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                            strcpy(entry->celname, celvar);</span><br><span style="color: hsl(0, 100%, 40%);">-                      } else { /* Point to same place as the column name */</span><br><span style="color: hsl(0, 100%, 40%);">-                           entry->celname = (char *)entry + sizeof(*entry);</span><br><span style="color: hsl(0, 100%, 40%);">-                     }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (!ast_strlen_zero(staticvalue)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            entry->staticvalue = entry->celname + strlen(entry->celname) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                              strcpy(entry->staticvalue, staticvalue);</span><br><span style="color: hsl(0, 100%, 40%);">-                     }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                   SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                    SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                   SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                   SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                    /* Specification states that the octenlen should be the maximum number of bytes</span><br><span style="color: hsl(0, 100%, 40%);">-                  * returned in a char or binary column, but it seems that some drivers just set</span><br><span style="color: hsl(0, 100%, 40%);">-                  * it to NULL. (Bad Postgres! No biscuit!) */</span><br><span style="color: hsl(0, 100%, 40%);">-                   if (entry->octetlen == 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                            entry->octetlen = entry->size;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);</span><br><span style="color: hsl(0, 100%, 40%);">-                      /* Insert column info into column list */</span><br><span style="color: hsl(0, 100%, 40%);">-                       AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);</span><br><span style="color: hsl(0, 100%, 40%);">-                 res = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_odbc_release_obj(obj);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-              if (AST_LIST_FIRST(&(tableptr->columns)))</span><br><span style="color: hsl(0, 100%, 40%);">-                        AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);</span><br><span style="color: hsl(0, 100%, 40%);">-               else</span><br><span style="color: hsl(0, 100%, 40%);">-                    ast_free(tableptr);</span><br><span>  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  ast_config_destroy(cfg);</span><br><span style="color: hsl(0, 100%, 40%);">-        return res;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return construct_queries();</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int free_config(void)</span><br><span style="color: hsl(120, 100%, 40%);">+static void *mb_manager_thread(void *data)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct tables *table;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct columns *entry;</span><br><span style="color: hsl(0, 100%, 40%);">-  while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {</span><br><span style="color: hsl(0, 100%, 40%);">-              while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_free(entry);</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span style="color: hsl(0, 100%, 40%);">-               ast_free(table);</span><br><span style="color: hsl(120, 100%, 40%);">+      int failed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_verb(3, "CEL ODBC Manager started.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       while (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_log(LOG_WARNING, "Unable to lock table list for modification. Retrying.\n");</span><br><span>   }</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     failed = load_config();</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        while (failed) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (wait_for_signal() == ETIMEDOUT) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 while (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_log(LOG_WARNING, "Unable to lock table list for modification. Retrying.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                    }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_verb(3, "Retrying %d failed table(s).\n", failed);</span><br><span style="color: hsl(120, 100%, 40%);">+                      failed = construct_queries();</span><br><span style="color: hsl(120, 100%, 40%);">+                 AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span style="color: hsl(120, 100%, 40%);">+          } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_verb(3, "Terminating CEL ODBC Manager.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+          }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_verb(3, "Terminating CEL ODBC Manager, no longer needed.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+static int start_table_manager(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+     int errcode = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_sem_init(&manager_sem, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if ((errcode = ast_pthread_create(&manager_thread, NULL, mb_manager_thread, NULL))) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void stop_table_manager(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       ast_sem_post(&manager_sem);</span><br><span style="color: hsl(120, 100%, 40%);">+       pthread_join(manager_thread, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   manager_thread = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_sem_destroy(&manager_sem);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Converting from const pointer to non-const (SQLPOINTER) is SAFE.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SQLBindParameter is designed for bi-directional data transfer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Logically there is no reason why a driver would clobber memory</span><br><span style="color: hsl(120, 100%, 40%);">+ * without asking and I have confirmed this hypothesis with the help</span><br><span style="color: hsl(120, 100%, 40%);">+ * of valgrind and a test. Valgrind did not report any memory</span><br><span style="color: hsl(120, 100%, 40%);">+ * violations and no segfault occured trying to write to</span><br><span style="color: hsl(120, 100%, 40%);">+ * write-protected memory.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * I also found that leaving all length-related parameters is fine.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The driver is able to deduce the size of our parameters just fine</span><br><span style="color: hsl(120, 100%, 40%);">+ * from the value and parameter types alone. */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void bind_parameter(</span><br><span style="color: hsl(120, 100%, 40%);">+      SQLHSTMT stmt,</span><br><span style="color: hsl(120, 100%, 40%);">+        const SQL_TIMESTAMP_STRUCT *event_time,</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct ast_cel_event_record *record,</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct tables *table,</span><br><span style="color: hsl(120, 100%, 40%);">+   int index,</span><br><span style="color: hsl(120, 100%, 40%);">+    int rva</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rva == offsetof(struct ast_cel_event_record, event_time)) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* Special case for timestamp. */</span><br><span style="color: hsl(120, 100%, 40%);">+             SQLBindParameter(stmt, index, SQL_PARAM_INPUT, SQL_C_TIMESTAMP,</span><br><span style="color: hsl(120, 100%, 40%);">+                       SQL_TYPE_TIMESTAMP, 0, 0, (SQLPOINTER) event_time, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (rva == offsetof(struct ast_cel_event_record, event_type)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                /* Special case for event type. */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!cel_show_user_def && record->event_type == AST_CEL_USER_DEFINED) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    SQLBindParameter(stmt, index, SQL_PARAM_INPUT, SQL_C_CHAR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            SQL_CHAR, 0, 0, (SQLPOINTER) record->user_defined_name, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      SQLBindParameter(stmt, index, SQL_PARAM_INPUT, SQL_C_CHAR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            SQL_CHAR, 0, 0, (SQLPOINTER) record->event_name, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+     } else if (rva == offsetof(struct ast_cel_event_record, amaflag)) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Special case for flags. */</span><br><span style="color: hsl(120, 100%, 40%);">+         SQLBindParameter(stmt, index, SQL_PARAM_INPUT, SQL_C_ULONG,</span><br><span style="color: hsl(120, 100%, 40%);">+                   SQL_INTEGER, 0, 0, (SQLPOINTER) &record->amaflag, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Obtain pointer to string value by adding rva offset to record base address */</span><br><span style="color: hsl(120, 100%, 40%);">+              char ** string_ptr = (char **) (((char *) record) + rva);</span><br><span style="color: hsl(120, 100%, 40%);">+             SQLBindParameter(stmt, index, SQL_PARAM_INPUT, SQL_C_CHAR,</span><br><span style="color: hsl(120, 100%, 40%);">+                    SQL_CHAR, 0, 0, (SQLPOINTER) *string_ptr, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void bind_parameters(</span><br><span style="color: hsl(120, 100%, 40%);">+  SQLHSTMT stmt,</span><br><span style="color: hsl(120, 100%, 40%);">+        const SQL_TIMESTAMP_STRUCT *event_time,</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct ast_cel_event_record *record,</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct tables *table</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+       struct columns *column;</span><br><span style="color: hsl(120, 100%, 40%);">+       int index = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      AST_LIST_TRAVERSE(&(table->columns), column, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (column->staticval) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   SQLBindParameter(stmt, index++, SQL_PARAM_INPUT, SQL_C_CHAR,</span><br><span style="color: hsl(120, 100%, 40%);">+                          SQL_CHAR, 0, 0, (SQLPOINTER) column->staticval, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+          } else if  (column->rva >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 bind_parameter(stmt, event_time, record, table, index++, column->rva);</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static SQLHSTMT prepare_query(</span><br><span style="color: hsl(120, 100%, 40%);">+        struct odbc_obj *obj,</span><br><span style="color: hsl(120, 100%, 40%);">+ void *data</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span>      int res, i;</span><br><span style="color: hsl(0, 100%, 40%);">-     char *sql = data;</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct prepare_params *params = data;</span><br><span>  SQLHSTMT stmt;</span><br><span>       SQLINTEGER nativeerror = 0, numfields = 0;</span><br><span>   SQLSMALLINT diagbytes = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-      unsigned char state[10], diagnostic[256];</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned char state[10], diagnostic[512];</span><br><span> </span><br><span>        res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);</span><br><span>      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span>@@ -325,9 +820,14 @@</span><br><span>                 return NULL;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   res = ast_odbc_prepare(obj, stmt, sql);</span><br><span style="color: hsl(0, 100%, 40%);">- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {</span><br><span style="color: hsl(0, 100%, 40%);">-           ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_debug(3, "Executing SQL statement: [%s]\n", params->table->query);</span><br><span style="color: hsl(120, 100%, 40%);">+        bind_parameters(stmt, &params->event_time, &params->record, params->table);</span><br><span style="color: hsl(120, 100%, 40%);">+  res = ast_odbc_prepare(obj, stmt, params->table->query);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return stmt;</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", params->table->query);</span><br><span>             SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);</span><br><span>          for (i = 0; i < numfields; i++) {</span><br><span>                         SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);</span><br><span>@@ -340,489 +840,128 @@</span><br><span>              SQLFreeHandle (SQL_HANDLE_STMT, stmt);</span><br><span>               return NULL;</span><br><span>         }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return stmt;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#define LENGTHEN_BUF1(size)                                                                                                                \</span><br><span style="color: hsl(0, 100%, 40%);">-                       do {                                                                                                                            \</span><br><span style="color: hsl(0, 100%, 40%);">-                               /* Lengthen buffer, if necessary */                                                             \</span><br><span style="color: hsl(0, 100%, 40%);">-                               if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) {            \</span><br><span style="color: hsl(0, 100%, 40%);">-                                       if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \</span><br><span style="color: hsl(0, 100%, 40%);">-                                            ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \</span><br><span style="color: hsl(0, 100%, 40%);">-                                             ast_free(sql);                                                                                  \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_free(sql2);                                                                                 \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               AST_RWLIST_UNLOCK(&odbc_tables);                                            \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               return;                                                                                                 \</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }                                                                                                                       \</span><br><span style="color: hsl(0, 100%, 40%);">-                               }                                                                                                                               \</span><br><span style="color: hsl(0, 100%, 40%);">-                       } while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+static int event_filtered_out(</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct tables *table,</span><br><span style="color: hsl(120, 100%, 40%);">+   const struct ast_cel_event_record *record</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Adding a filter means that only events matching the filter will</span><br><span style="color: hsl(120, 100%, 40%);">+     * be written to the table. Multiple filters behave like a logical AND,</span><br><span style="color: hsl(120, 100%, 40%);">+        * meaning the event must match all filters. */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#define LENGTHEN_BUF2(size)                                                                                                          \</span><br><span style="color: hsl(0, 100%, 40%);">-                       do {                                                                                                                            \</span><br><span style="color: hsl(0, 100%, 40%);">-                               if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) {          \</span><br><span style="color: hsl(0, 100%, 40%);">-                                       if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \</span><br><span style="color: hsl(0, 100%, 40%);">-                                          ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \</span><br><span style="color: hsl(0, 100%, 40%);">-                                             ast_free(sql);                                                                                  \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_free(sql2);                                                                                 \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               AST_RWLIST_UNLOCK(&odbc_tables);                                            \</span><br><span style="color: hsl(0, 100%, 40%);">-                                               return;                                                                                                 \</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }                                                                                                                       \</span><br><span style="color: hsl(0, 100%, 40%);">-                               }                                                                                                                               \</span><br><span style="color: hsl(0, 100%, 40%);">-                       } while (0)</span><br><span style="color: hsl(120, 100%, 40%);">+   struct filters *filter;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_LIST_TRAVERSE(&(table->filters), filter, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+           if (filter->rva == offsetof(struct ast_cel_event_record, event_time)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* Filtering timestamps is ignored because pointless. */</span><br><span style="color: hsl(120, 100%, 40%);">+              } else if (filter->rva == offsetof(struct ast_cel_event_record, event_type)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* Special case for event type. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!cel_show_user_def && record->event_type == AST_CEL_USER_DEFINED) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            if (!ast_strings_equal(record->user_defined_name, filter->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                     return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              if (!ast_strings_equal(record->event_name, filter->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                    return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (filter->rva == offsetof(struct ast_cel_event_record, amaflag)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        /* Special case for flags. */</span><br><span style="color: hsl(120, 100%, 40%);">+                 int len = snprintf(NULL, 0, "%u", record->amaflag) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                  char * buffer = ast_alloca(len);</span><br><span style="color: hsl(120, 100%, 40%);">+                      snprintf(buffer, len, "%u", record->amaflag);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (!ast_strings_equal(buffer, filter->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                           return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             } else if (filter->rva >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  /* Obtain pointer to CEL string variable by adding rva to</span><br><span style="color: hsl(120, 100%, 40%);">+                      * base address of CEL record. Then obtain string value by</span><br><span style="color: hsl(120, 100%, 40%);">+                     * dereferencing that pointer. */</span><br><span style="color: hsl(120, 100%, 40%);">+                     const char * value = *(const char **) (((const char *) record) + filter->rva);</span><br><span style="color: hsl(120, 100%, 40%);">+                     if (!ast_strings_equal(value, filter->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                            return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void timeval_to_sql_timestamp(</span><br><span style="color: hsl(120, 100%, 40%);">+ SQL_TIMESTAMP_STRUCT * timestamp,</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct timeval * event_time,</span><br><span style="color: hsl(120, 100%, 40%);">+    const int usegmtime</span><br><span style="color: hsl(120, 100%, 40%);">+) {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct timeval date_tv = *event_time;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_tm tm = { 0, };</span><br><span style="color: hsl(120, 100%, 40%);">+    ast_localtime(&date_tv, &tm, usegmtime ? "UTC" : NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   timestamp->year = tm.tm_year + 1900;</span><br><span style="color: hsl(120, 100%, 40%);">+       timestamp->month = tm.tm_mon + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+  timestamp->day = tm.tm_mday;</span><br><span style="color: hsl(120, 100%, 40%);">+       timestamp->hour = tm.tm_hour;</span><br><span style="color: hsl(120, 100%, 40%);">+      timestamp->minute = tm.tm_min;</span><br><span style="color: hsl(120, 100%, 40%);">+     timestamp->second = tm.tm_sec;</span><br><span style="color: hsl(120, 100%, 40%);">+     timestamp->fraction = tm.tm_usec;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> static void odbc_log(struct ast_event *event)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-       struct tables *tableptr;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct columns *entry;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct tables *table;</span><br><span>        struct odbc_obj *obj;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);</span><br><span style="color: hsl(0, 100%, 40%);">-        char *tmp;</span><br><span style="color: hsl(0, 100%, 40%);">-      char colbuf[1024], *colptr;</span><br><span>  SQLHSTMT stmt = NULL;</span><br><span>        SQLLEN rows = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct ast_cel_event_record record = {</span><br><span style="color: hsl(0, 100%, 40%);">-          .version = AST_CEL_EVENT_RECORD_VERSION,</span><br><span style="color: hsl(0, 100%, 40%);">-        };</span><br><span style="color: hsl(120, 100%, 40%);">+    struct prepare_params params;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_cel_fill_record(event, &record)) {</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+     params.record.version = AST_CEL_EVENT_RECORD_VERSION;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       if (!sql || !sql2) {</span><br><span style="color: hsl(0, 100%, 40%);">-            if (sql)</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_free(sql);</span><br><span style="color: hsl(0, 100%, 40%);">-          if (sql2)</span><br><span style="color: hsl(0, 100%, 40%);">-                       ast_free(sql2);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_cel_fill_record(event, &params.record)) {</span><br><span>                return;</span><br><span>      }</span><br><span> </span><br><span>        if (AST_RWLIST_RDLOCK(&odbc_tables)) {</span><br><span>           ast_log(LOG_ERROR, "Unable to lock table list.  Insert CEL(s) failed.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_free(sql);</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_free(sql2);</span><br><span>              return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-           int first = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-          ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_str_set(&sql2, 0, " VALUES (");</span><br><span style="color: hsl(120, 100%, 40%);">+     AST_LIST_TRAVERSE(&odbc_tables, table, list) {</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-          /* No need to check the connection now; we'll handle any failure in prepare_and_execute */</span><br><span style="color: hsl(0, 100%, 40%);">-          if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_log(LOG_WARNING, "Unable to retrieve database handle for '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));</span><br><span style="color: hsl(120, 100%, 40%);">+                if (ast_strlen_zero(table->query)) {</span><br><span>                      continue;</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {</span><br><span style="color: hsl(0, 100%, 40%);">-                   int datefield = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                      int unknown = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                        if (strcasecmp(entry->celname, "eventtime") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                datefield = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                  }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       /* Check if we have a similarly named variable */</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (entry->staticvalue) {</span><br><span style="color: hsl(0, 100%, 40%);">-                            colptr = ast_strdupa(entry->staticvalue);</span><br><span style="color: hsl(0, 100%, 40%);">-                    } else if (datefield) {</span><br><span style="color: hsl(0, 100%, 40%);">-                         struct timeval date_tv = record.event_time;</span><br><span style="color: hsl(0, 100%, 40%);">-                             struct ast_tm tm = { 0, };</span><br><span style="color: hsl(0, 100%, 40%);">-                              ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                          /* SQL server 2008 added datetime2 and datetimeoffset data types, that</span><br><span style="color: hsl(0, 100%, 40%);">-                             are reported to SQLColumns() as SQL_WVARCHAR, according to "Enhanced</span><br><span style="color: hsl(0, 100%, 40%);">-                               Date/Time Type Behavior with Previous SQL Server Versions (ODBC)".</span><br><span style="color: hsl(0, 100%, 40%);">-                                 Here we format the event time with fraction seconds, so these new</span><br><span style="color: hsl(0, 100%, 40%);">-                               column types will be set to high-precision event time. However, 'date'</span><br><span style="color: hsl(0, 100%, 40%);">-                                  and 'time' columns, also newly introduced, reported as SQL_WVARCHAR</span><br><span style="color: hsl(0, 100%, 40%);">-                             too, and insertion of the value formatted here into these will fail.</span><br><span style="color: hsl(0, 100%, 40%);">-                            This should be ok, however, as nobody is going to store just event</span><br><span style="color: hsl(0, 100%, 40%);">-                              date or just time for CDR purposes.</span><br><span style="color: hsl(0, 100%, 40%);">-                           */</span><br><span style="color: hsl(0, 100%, 40%);">-                             ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S.%6q", &tm);</span><br><span style="color: hsl(0, 100%, 40%);">-                               colptr = colbuf;</span><br><span style="color: hsl(0, 100%, 40%);">-                        } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                if (strcmp(entry->celname, "userdeftype") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                  ast_copy_string(colbuf, record.user_defined_name, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                              } else if (strcmp(entry->celname, "cid_name") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_copy_string(colbuf, record.caller_id_name, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                         } else if (strcmp(entry->celname, "cid_num") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_copy_string(colbuf, record.caller_id_num, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                          } else if (strcmp(entry->celname, "cid_ani") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_copy_string(colbuf, record.caller_id_ani, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                          } else if (strcmp(entry->celname, "cid_rdnis") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                     ast_copy_string(colbuf, record.caller_id_rdnis, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                                } else if (strcmp(entry->celname, "cid_dnid") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_copy_string(colbuf, record.caller_id_dnid, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                         } else if (strcmp(entry->celname, "exten") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                 ast_copy_string(colbuf, record.extension, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                              } else if (strcmp(entry->celname, "context") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_copy_string(colbuf, record.context, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                                } else if (strcmp(entry->celname, "channame") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_copy_string(colbuf, record.channel_name, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                           } else if (strcmp(entry->celname, "appname") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_copy_string(colbuf, record.application_name, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                               } else if (strcmp(entry->celname, "appdata") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_copy_string(colbuf, record.application_data, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                               } else if (strcmp(entry->celname, "accountcode") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                   ast_copy_string(colbuf, record.account_code, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                           } else if (strcmp(entry->celname, "peeraccount") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                   ast_copy_string(colbuf, record.peer_account, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                           } else if (strcmp(entry->celname, "uniqueid") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_copy_string(colbuf, record.unique_id, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                              } else if (strcmp(entry->celname, "linkedid") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      ast_copy_string(colbuf, record.linked_id, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                              } else if (strcmp(entry->celname, "userfield") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                     ast_copy_string(colbuf, record.user_field, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                             } else if (strcmp(entry->celname, "peer") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                  ast_copy_string(colbuf, record.peer, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                           } else if (strcmp(entry->celname, "amaflags") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                      snprintf(colbuf, sizeof(colbuf), "%u", record.amaflag);</span><br><span style="color: hsl(0, 100%, 40%);">-                               } else if (strcmp(entry->celname, "extra") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                 ast_copy_string(colbuf, record.extra, sizeof(colbuf));</span><br><span style="color: hsl(0, 100%, 40%);">-                          } else if (strcmp(entry->celname, "eventtype") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                     snprintf(colbuf, sizeof(colbuf), "%u", record.event_type);</span><br><span style="color: hsl(0, 100%, 40%);">-                            } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                        colbuf[0] = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                  unknown = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                            }</span><br><span style="color: hsl(0, 100%, 40%);">-                               colptr = colbuf;</span><br><span style="color: hsl(0, 100%, 40%);">-                        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (colptr && !unknown) {</span><br><span style="color: hsl(0, 100%, 40%);">-                               /* Check first if the column filters this entry.  Note that this</span><br><span style="color: hsl(0, 100%, 40%);">-                                 * is very specifically NOT ast_strlen_zero(), because the filter</span><br><span style="color: hsl(0, 100%, 40%);">-                                * could legitimately specify that the field is blank, which is</span><br><span style="color: hsl(0, 100%, 40%);">-                          * different from the field being unspecified (NULL). */</span><br><span style="color: hsl(0, 100%, 40%);">-                                if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                  ast_verb(4, "CEL column '%s' with value '%s' does not match filter of"</span><br><span style="color: hsl(0, 100%, 40%);">-                                                " '%s'.  Cancelling this CEL.\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                             entry->celname, colptr, entry->filtervalue);</span><br><span style="color: hsl(0, 100%, 40%);">-                                      goto early_release;</span><br><span style="color: hsl(0, 100%, 40%);">-                             }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                               /* Only a filter? */</span><br><span style="color: hsl(0, 100%, 40%);">-                            if (ast_strlen_zero(entry->name))</span><br><span style="color: hsl(0, 100%, 40%);">-                                    continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                               LENGTHEN_BUF1(strlen(entry->name));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                          switch (entry->type) {</span><br><span style="color: hsl(0, 100%, 40%);">-                               case SQL_CHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_VARCHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-                               case SQL_LONGVARCHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-#ifdef HAVE_ODBC_WCHAR</span><br><span style="color: hsl(0, 100%, 40%);">-                             case SQL_WCHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-                         case SQL_WVARCHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-                              case SQL_WLONGVARCHAR:</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span style="color: hsl(0, 100%, 40%);">-                            case SQL_BINARY:</span><br><span style="color: hsl(0, 100%, 40%);">-                                case SQL_VARBINARY:</span><br><span style="color: hsl(0, 100%, 40%);">-                             case SQL_LONGVARBINARY:</span><br><span style="color: hsl(0, 100%, 40%);">-                         case SQL_GUID:</span><br><span style="color: hsl(0, 100%, 40%);">-                                  /* For these two field names, get the rendered form, instead of the raw</span><br><span style="color: hsl(0, 100%, 40%);">-                                  * form (but only when we're dealing with a character-based field).</span><br><span style="color: hsl(0, 100%, 40%);">-                                  */</span><br><span style="color: hsl(0, 100%, 40%);">-                                     if (strcasecmp(entry->name, "eventtype") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                           const char *event_name;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                         event_name = (!cel_show_user_def</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        && record.event_type == AST_CEL_USER_DEFINED)</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   ? record.user_defined_name : record.event_name;</span><br><span style="color: hsl(0, 100%, 40%);">-                                         snprintf(colbuf, sizeof(colbuf), "%s", event_name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                       /* Truncate too-long fields */</span><br><span style="color: hsl(0, 100%, 40%);">-                                  if (entry->type != SQL_GUID) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               if (strlen(colptr) > entry->octetlen) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   colptr[entry->octetlen] = '\0';</span><br><span style="color: hsl(0, 100%, 40%);">-                                              }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                    LENGTHEN_BUF2(strlen(colptr));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                  /* Encode value, with escaping */</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_str_append(&sql2, 0, "%s'", first ? "" : ",");</span><br><span style="color: hsl(0, 100%, 40%);">-                                        for (tmp = colptr; *tmp; tmp++) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               if (*tmp == '\'') {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 ast_str_append(&sql2, 0, "''");</span><br><span style="color: hsl(0, 100%, 40%);">-                                           } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 ast_str_append(&sql2, 0, "\\\\");</span><br><span style="color: hsl(0, 100%, 40%);">-                                         } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        ast_str_append(&sql2, 0, "%c", *tmp);</span><br><span style="color: hsl(0, 100%, 40%);">-                                             }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_str_append(&sql2, 0, "'");</span><br><span style="color: hsl(0, 100%, 40%);">-                                        break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_TYPE_DATE:</span><br><span style="color: hsl(0, 100%, 40%);">-                                     if (ast_strlen_zero(colptr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                int year = 0, month = 0, day = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               if (strcasecmp(entry->name, "eventdate") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   struct ast_tm tm;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        year = tm.tm_year + 1900;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       month = tm.tm_mon + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                  day = tm.tm_mday;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                            month <= 0 || month > 12 || day < 0 || day > 31 ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                          ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               (month == 2 && year % 400 == 0 && day > 29) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               (month == 2 && year % 100 == 0 && day > 28) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               (month == 2 && year % 4 == 0 && day > 29) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                         (month == 2 && year % 4 != 0 && day > 28)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                         ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       if (year > 0 && year < 100) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                             year += 2000;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   }</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(17);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", first ? "" : ",", year, month, day);</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_TYPE_TIME:</span><br><span style="color: hsl(0, 100%, 40%);">-                                     if (ast_strlen_zero(colptr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                int hour = 0, minute = 0, second = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                           if (strcasecmp(entry->name, "eventdate") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                   struct ast_tm tm;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        hour = tm.tm_hour;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      minute = tm.tm_min;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;</span><br><span style="color: hsl(0, 100%, 40%);">-                                             } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > (tableptr->allowleapsec ? 60 : 59)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(15);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", first ? "" : ",", hour, minute, second);</span><br><span style="color: hsl(0, 100%, 40%);">-                                   }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_TYPE_TIMESTAMP:</span><br><span style="color: hsl(0, 100%, 40%);">-                                case SQL_TIMESTAMP:</span><br><span style="color: hsl(0, 100%, 40%);">-                                     if (ast_strlen_zero(colptr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                          continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                       } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                if (datefield) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        /*</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       * We've already properly formatted the timestamp so there's no need</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     * to parse it and re-format it.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                         */</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    LENGTHEN_BUF2(27);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      ast_str_append(&sql2, 0, "%s{ts '%s'}", first ? "" : ",", colptr);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        int year = 0, month = 0, day = 0, hour = 0, minute = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 /* MUST use double for microsecond precision */</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 double second = 0.0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    if (strcasecmp(entry->name, "eventdate") == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           /*</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               * There doesn't seem to be any reference to 'eventdate' anywhere</span><br><span style="color: hsl(0, 100%, 40%);">-                                                            * other than in this module.  It should be considered for removal</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               * at a later date.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                              */</span><br><span style="color: hsl(0, 100%, 40%);">-                                                             struct ast_tm tm;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                year = tm.tm_year + 1900;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               month = tm.tm_mon + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                          day = tm.tm_mday;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               hour = tm.tm_hour;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                              minute = tm.tm_min;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                             second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                             second += (tm.tm_usec / 1000000.0);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                /*</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               * If we're here, the data to be inserted MAY be a timestamp</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                 * but the column is.  We parse as much as we can.</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               */</span><br><span style="color: hsl(0, 100%, 40%);">-                                                             int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%lf", &year, &month, &day, &hour, &minute, &second);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                                          if ((count != 3 && count != 5 && count != 6) || year <= 0 ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                 month <= 0 || month > 12 || day < 0 || day > 31 ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                  ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                       (month == 2 && year % 400 == 0 && day > 29) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                       (month == 2 && year % 100 == 0 && day > 28) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                       (month == 2 && year % 4 == 0 && day > 29) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                 (month == 2 && year % 4 != 0 && day > 28) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                 hour > 23 || minute > 59 || ((int)floor(second)) > (tableptr->allowleapsec ? 60 : 59) ||</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                    hour < 0 || minute < 0 || ((int)floor(second)) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                  ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                   continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                                               if (year > 0 && year < 100) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                                     year += 2000;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                           }</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    LENGTHEN_BUF2(27);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%09.6lf'}", first ? "" : ",", year, month, day, hour, minute, second);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_INTEGER:</span><br><span style="color: hsl(0, 100%, 40%);">-                                       {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               int integer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                if (sscanf(colptr, "%30d", &integer) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(12);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_BIGINT:</span><br><span style="color: hsl(0, 100%, 40%);">-                                        {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               long long integer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                          int ret;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                if ((ret = sscanf(colptr, "%30lld", &integer)) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    ast_log(LOG_WARNING, "CEL variable %s is not an integer. (%d - '%s')\n", entry->name, ret, colptr);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                        continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(24);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);</span><br><span style="color: hsl(0, 100%, 40%);">-                                        }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_SMALLINT:</span><br><span style="color: hsl(0, 100%, 40%);">-                                      {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               short integer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                              if (sscanf(colptr, "%30hd", &integer) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                     ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(7);</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_TINYINT:</span><br><span style="color: hsl(0, 100%, 40%);">-                                       {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               signed char integer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                if (sscanf(colptr, "%30hhd", &integer) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(4);</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_BIT:</span><br><span style="color: hsl(0, 100%, 40%);">-                                   {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               signed char integer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                if (sscanf(colptr, "%30hhd", &integer) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-                                               if (integer != 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                                                       integer = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                            ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(2);</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_NUMERIC:</span><br><span style="color: hsl(0, 100%, 40%);">-                               case SQL_DECIMAL:</span><br><span style="color: hsl(0, 100%, 40%);">-                                       {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               double number = 0.0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                            if (sscanf(colptr, "%30lf", &number) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(entry->decimals + 2);</span><br><span style="color: hsl(0, 100%, 40%);">-                                          ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_FLOAT:</span><br><span style="color: hsl(0, 100%, 40%);">-                         case SQL_REAL:</span><br><span style="color: hsl(0, 100%, 40%);">-                          case SQL_DOUBLE:</span><br><span style="color: hsl(0, 100%, 40%);">-                                        {</span><br><span style="color: hsl(0, 100%, 40%);">-                                               double number = 0.0;</span><br><span style="color: hsl(0, 100%, 40%);">-                                            if (sscanf(colptr, "%30lf", &number) != 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                      ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                                    continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                                               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                            LENGTHEN_BUF2(entry->decimals);</span><br><span style="color: hsl(0, 100%, 40%);">-                                              ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);</span><br><span style="color: hsl(0, 100%, 40%);">-                                  }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       break;</span><br><span style="color: hsl(0, 100%, 40%);">-                          default:</span><br><span style="color: hsl(0, 100%, 40%);">-                                        ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);</span><br><span style="color: hsl(0, 100%, 40%);">-                                        continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                               }</span><br><span style="color: hsl(0, 100%, 40%);">-                               first = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             if (event_filtered_out(table, &params.record)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           /* Concatenate the two constructed buffers */</span><br><span style="color: hsl(0, 100%, 40%);">-           LENGTHEN_BUF1(ast_str_strlen(sql2));</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_str_append(&sql, 0, ")");</span><br><span style="color: hsl(0, 100%, 40%);">-             ast_str_append(&sql2, 0, ")");</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));</span><br><span style="color: hsl(120, 100%, 40%);">+            /* No need to check the connection now; we'll handle any failure in prepare_and_execute */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!(obj = ast_odbc_request_obj(table->connection, 0))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 ast_log(LOG_WARNING, "Unable to retrieve database handle for '%s:%s'.  CEL failed: %s\n", table->connection, table->name, table->query);</span><br><span style="color: hsl(120, 100%, 40%);">+                   continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           ast_debug(3, "Executing SQL statement: [%s]\n", ast_str_buffer(sql));</span><br><span style="color: hsl(0, 100%, 40%);">-         stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));</span><br><span style="color: hsl(120, 100%, 40%);">+               params.table = table;</span><br><span style="color: hsl(120, 100%, 40%);">+         timeval_to_sql_timestamp(&params.event_time, &params.record.event_time, table->usegmtime);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               stmt = ast_odbc_prepare_and_execute(obj, prepare_query, &params);</span><br><span>                if (stmt) {</span><br><span>                  SQLRowCount(stmt, &rows);</span><br><span>                        SQLFreeHandle(SQL_HANDLE_STMT, stmt);</span><br><span>                }</span><br><span>            if (rows == 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        ast_log(LOG_WARNING, "Insert failed on '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_WARNING, "Insert failed on '%s:%s'.  CEL failed: %s\n", table->connection, table->name, table->query);</span><br><span>                }</span><br><span style="color: hsl(0, 100%, 40%);">-early_release:</span><br><span>              ast_odbc_release_obj(obj);</span><br><span>   }</span><br><span>    AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* Next time, just allocate buffers that are that big to start with. */</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_str_strlen(sql) > maxsize) {</span><br><span style="color: hsl(0, 100%, 40%);">-         maxsize = ast_str_strlen(sql);</span><br><span style="color: hsl(0, 100%, 40%);">-  }</span><br><span style="color: hsl(0, 100%, 40%);">-       if (ast_str_strlen(sql2) > maxsize2) {</span><br><span style="color: hsl(0, 100%, 40%);">-               maxsize2 = ast_str_strlen(sql2);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       ast_free(sql);</span><br><span style="color: hsl(0, 100%, 40%);">-  ast_free(sql2);</span><br><span> }</span><br><span> </span><br><span> static int unload_module(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- if (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          return -1;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    ast_cel_backend_unregister(ODBC_BACKEND_NAME);</span><br><span style="color: hsl(0, 100%, 40%);">-  free_config();</span><br><span style="color: hsl(0, 100%, 40%);">-  AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span style="color: hsl(120, 100%, 40%);">+  stop_table_manager();</span><br><span style="color: hsl(120, 100%, 40%);">+ destroy_tables_safely();</span><br><span>     AST_RWLIST_HEAD_DESTROY(&odbc_tables);</span><br><span> </span><br><span>       return 0;</span><br><span>@@ -832,34 +971,30 @@</span><br><span> {</span><br><span>       AST_RWLIST_HEAD_INIT(&odbc_tables);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     if (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-       load_config();</span><br><span style="color: hsl(0, 100%, 40%);">-  AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span>         if (ast_cel_backend_register(ODBC_BACKEND_NAME, odbc_log)) {</span><br><span style="color: hsl(0, 100%, 40%);">-            ast_log(LOG_ERROR, "Unable to subscribe to CEL events\n");</span><br><span style="color: hsl(0, 100%, 40%);">-            free_config();</span><br><span style="color: hsl(120, 100%, 40%);">+                ast_log(LOG_ERROR, "Unable to subscribe to CEL events.\n");</span><br><span>                return AST_MODULE_LOAD_DECLINE;</span><br><span>      }</span><br><span style="color: hsl(0, 100%, 40%);">-       return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (start_table_manager()) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_cel_backend_unregister(ODBC_BACKEND_NAME);</span><br><span style="color: hsl(120, 100%, 40%);">+                AST_RWLIST_HEAD_DESTROY(&odbc_tables);</span><br><span style="color: hsl(120, 100%, 40%);">+            return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span> }</span><br><span> </span><br><span> static int reload(void)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-      if (AST_RWLIST_WRLOCK(&odbc_tables)) {</span><br><span style="color: hsl(0, 100%, 40%);">-              ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");</span><br><span style="color: hsl(0, 100%, 40%);">-          return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+     stop_table_manager();</span><br><span style="color: hsl(120, 100%, 40%);">+ destroy_tables_safely();</span><br><span style="color: hsl(120, 100%, 40%);">+      start_table_manager();</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      free_config();</span><br><span style="color: hsl(0, 100%, 40%);">-  load_config();</span><br><span style="color: hsl(0, 100%, 40%);">-  AST_RWLIST_UNLOCK(&odbc_tables);</span><br><span>         return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "ODBC CEL backend",</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, ODBC_BACKEND_NAME,</span><br><span>        .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span>    .load = load_module,</span><br><span>         .unload = unload_module,</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14698">change 14698</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/14698"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-Change-Id: Id85d81add33096f8282d212daf239f2fc845d783 </div>
<div style="display:none"> Gerrit-Change-Number: 14698 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Dennis <dennis.buteyn@xorcom.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>