=== UPGRADE-10.txt -- Upgrade info for 1.8 to 10
===
===========================================================
+
from 11.8 to 11.9
* res_fax now returns the correct rates for V.27ter (4800 or 9600 bit/s).
Because of this the default settings would not load, so the minrate (minimum
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
a sound to the conference when waitmarked users are moved into the conference.
+* 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)
From 11.7 to 11.8:
* The per console verbose level feature as previously implemented caused a
; 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;
SQLHSTMT stmt;
char sql[256];
SQLLEN rowcount=0;
- const char *newparam;
- int res, count = 1;
+ const char *newparam, *newval;
+ int res, count = 0, paramcount = 0;
va_list aq;
struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
struct odbc_cache_tables *tableptr;
struct odbc_cache_columns *column = NULL;
struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
- if (!table) {
+ if (!table || !keyfield) {
return -1;
}
return -1;
}
- va_copy(aq, ap);
- newparam = va_arg(aq, const char *);
- if (!newparam) {
- va_end(aq);
- ast_odbc_release_obj(obj);
- ast_odbc_release_table(tableptr);
- ast_string_field_free_memory(&cps);
- return -1;
+ 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);
}
- va_arg(aq, const char *);
- if (tableptr && !ast_odbc_find_column(tableptr, newparam)) {
- ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'. Update will fail\n", newparam, table, database);
- }
+ va_copy(aq, ap);
- snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
+ snprintf(sql, sizeof(sql), "UPDATE %s SET ", table);
while((newparam = va_arg(aq, const char *))) {
- va_arg(aq, const char *);
- if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
- /* NULL test for integer-based columns */
- if (ast_strlen_zero(newparam) && 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", newparam);
+ newval = va_arg(aq, const char *);
+ if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count >= 64) {
+ if (paramcount++) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", ");
+ }
+ /* NULL test for non-text columns */
+ if (count < 64 && ast_strlen_zero(newval) && column->nullable && !is_text(column) && !ast_odbc_allow_empty_string_in_nontext(obj)) {
+ snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=NULL", newparam);
cps.skip |= (1LL << count);
} else {
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
+ /* 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=?", newparam);
}
} else { /* the column does not exist in the table */
cps.skip |= (1LL << count);
}
- count++;
+ ++count;
}
va_end(aq);
snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
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;