===
===========================================================
From 12.1.0 to 12.2.0:
+IAX2:
+ - When communicating with a peer on an Asterisk 1.4 or earlier system, the
+ chan_iax2 parameter 'connectedline' must be set to "no" in iax.conf. This
+ prevents an incompatible connected line frame from an Astersik 1.8 or later
+ system from causing a hangup in an Asterisk 1.4 or earlier system. Note that
+ this particular incompatibility has always existed between 1.4 and 1.8 and
+ later versions; this upgrade note is simply informing users of its existance.
+
+ODBC:
+- A compatibility setting, allow_empty_string_in_nontext, has been added to
+ res_odbc.conf. When enabled (default behavior), empty column values are
+ stored as empty strings during realtime updates. Disabling this option
+ causes empty column values to be stored as NULLs for non-text columns.
+
+ Disable it for PostgreSQL backends in order to avoid errors caused by
+ updating integer columns with an empty string instead of NULL
+ (sipppeers,sipregs)
+
PJSIP:
- The PJSIP registrar now stores the contents of the User-Agent header of incoming
REGISTER requests for each contact that is registered. If using realtime for
- PJSIP endpoints now have a "message_context" option that can be used to determine
where to route incoming MESSAGE requests from the endpoint.
-IAX2:
- - When communicating with a peer on an Asterisk 1.4 or earlier system, the
- chan_iax2 parameter 'connectedline' must be set to "no" in iax.conf. This
- prevents an incompatible connected line frame from an Astersik 1.8 or later
- system from causing a hangup in an Asterisk 1.4 or earlier system. Note that
- this particular incompatibility has always existed between 1.4 and 1.8 and
- later versions; this upgrade note is simply informing users of its existance.
-
Realtime Configuration:
- PJSIP endpoint columns 'tos_audio' and 'tos_video' have been changed from yes/no
enumerators to string values. 'cos_audio' and 'cos_video' have been changed from
a yes/no enumerator to an integer value.
From 12.0.0 to 12.1.0:
-* The sound_place_into_conference sound used in Confbridge is now deprecated
+- The sound_place_into_conference sound used in Confbridge is now deprecated
and is no longer functional since it has been broken since its inception
and the fix involved using a different method to achieve the same goal. The
new method to achieve this functionality is by using sound_begin to play
; MS SQL Server, the answer is no.
;backslash_is_escape => yes
;
+; When enabled (default behavior), empty column values are stored as empty strings
+; during realtime updates. Disabling this option causes empty column values to be
+; stored as NULLs for non-text columns.
+; Disable it for PostgreSQL backend in order to avoid errors caused by updating
+; integer columns with an empty string instead of NULL (sipppeers,sipregs)
+; NOTE: This option will be removed in asterisk 13. At that point, it will always behave as if it was set to 'no'.
+;allow_empty_string_in_nontext => yes
+;
; How long (in seconds) should we attempt to connect before considering the
; connection dead? The default is 10 seconds, but you may wish to reduce it,
; to increase responsiveness.
*/
SQLRETURN ast_odbc_ast_str_SQLGetData(struct ast_str **buf, int pmaxlen, SQLHSTMT StatementHandle, SQLUSMALLINT ColumnNumber, SQLSMALLINT TargetType, SQLLEN *StrLen_or_Ind);
+/*! \brief Checks if the database natively supports implicit conversion from an empty string to a number (0).
+ * \param obj The ODBC object
+ * \return Returns 1 if the implicit conversion is valid and non-text columns can take empty strings, 0 if the conversion is not valid and NULLs should be used instead '\'
+ */
+int ast_odbc_allow_empty_string_in_nontext(struct odbc_obj *obj);
+
#endif /* _ASTERISK_RES_ODBC_H */
}
}
+static inline int is_text(const struct odbc_cache_columns *column)
+{
+ return column->type == SQL_CHAR || column->type == SQL_VARCHAR || column->type == SQL_LONGVARCHAR
+ || column->type == SQL_WCHAR || column->type == SQL_WVARCHAR || column->type == SQL_WLONGVARCHAR;
+}
+
static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
{
int res, x = 1, count = 0;
char sql[256];
SQLLEN rowcount=0;
const struct ast_variable *field = fields;
- int res, count = 1;
+ int res, count = 0, paramcount = 0;
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup, .fields = fields, };
struct odbc_cache_tables *tableptr;
struct odbc_cache_columns *column = NULL;
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
- if (!table || !field) {
+ if (!table || !field || !keyfield) {
return -1;
}
return -1;
}
- if (tableptr && !ast_odbc_find_column(tableptr, field->name)) {
- ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", field->name, table, database);
+
+ if (tableptr && !ast_odbc_find_column(tableptr, keyfield)) {
+ ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", keyfield, table, database);
}
- snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, field->name);
+ snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
while ((field = field->next)) {
- if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count > 63) {
- /* NULL test for integer-based columns */
- if (ast_strlen_zero(field->name) && tableptr && column && column->nullable && count < 64 &&
- (column->type == SQL_INTEGER || column->type == SQL_BIGINT ||
- column->type == SQL_SMALLINT || column->type == SQL_TINYINT ||
- column->type == SQL_NUMERIC || column->type == SQL_DECIMAL)) {
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=NULL", field->name);
+ if ((tableptr && (column = ast_odbc_find_column(tableptr, field->name))) || count >= 64) {
+ if (paramcount++) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
+ }
+ /* NULL test for non-text columns */
+ if (count < 64 && ast_strlen_zero(field->value) && column->nullable && !is_text(column) && !ast_odbc_allow_empty_string_in_nontext(obj)) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", field->name);
cps.skip |= (1LL << count);
} else {
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", field->name);
+ /* Value is not an empty string, or column accepts empty strings, or we couldn't fit any more into cps.skip (count >= 64 ?!). */
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", field->name);
}
} else { /* the column does not exist in the table */
cps.skip |= (1LL << count);
}
- count++;
+ ++count;
+ field = field->next;
}
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
ast_odbc_release_table(tableptr);
unsigned int delme:1; /*!< Purge the class */
unsigned int backslash_is_escape:1; /*!< On this database, the backslash is a native escape sequence */
unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
+ unsigned int allow_empty_strings:1; /*!< Implicit conversion from an empty string to a number is valid for this database */
unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
unsigned int limit; /*!< Maximum number of database handles we will allow */
int count; /*!< Running count of pooled connections */
struct ast_variable *v;
char *cat;
const char *dsn, *username, *password, *sanitysql;
- int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
+ int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation, allow_empty_strings;
struct timeval ncache = { 0, 0 };
unsigned int idlecheck;
int preconnect = 0, res = 0;
bse = 1;
conntimeout = 10;
forcecommit = 0;
+ allow_empty_strings = 1;
isolation = SQL_TXN_READ_COMMITTED;
for (v = ast_variable_browse(config, cat); v; v = v->next) {
if (!strcasecmp(v->name, "pooling")) {
sanitysql = v->value;
} else if (!strcasecmp(v->name, "backslash_is_escape")) {
bse = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "allow_empty_string_in_nontext")) {
+ allow_empty_strings = ast_true(v->value);
} else if (!strcasecmp(v->name, "connect_timeout")) {
if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
new->idlecheck = idlecheck;
new->conntimeout = conntimeout;
new->negative_connection_cache = ncache;
+ new->allow_empty_strings = allow_empty_strings ? 1 : 0;
if (cat)
ast_copy_string(new->name, cat, sizeof(new->name));
return obj->parent->backslash_is_escape;
}
+int ast_odbc_allow_empty_string_in_nontext(struct odbc_obj *obj)
+{
+ return obj->parent->allow_empty_strings;
+}
+
static int commit_exec(struct ast_channel *chan, const char *data)
{
struct odbc_txn_frame *tx;
global:
LINKER_SYMBOL_PREFIXast_odbc_ast_str_SQLGetData;
LINKER_SYMBOL_PREFIXast_odbc_backslash_is_escape;
+ LINKER_SYMBOL_PREFIXast_odbc_allow_empty_string_in_nontext;
LINKER_SYMBOL_PREFIXast_odbc_clear_cache;
LINKER_SYMBOL_PREFIXast_odbc_direct_execute;
LINKER_SYMBOL_PREFIXast_odbc_find_column;