<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, ¶ms->event_time, ¶ms->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, ¶ms.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, ¶ms.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(¶ms.event_time, ¶ms.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, ¶ms);</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>