MXS-1275: Function name mappings need to follow sql_mode

This commit is contained in:
Johan Wikman
2017-06-02 15:36:56 +03:00
parent a60b6473ed
commit d9448e1550
2 changed files with 152 additions and 94 deletions

View File

@ -74,30 +74,6 @@
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#define MYSQL_COM_QUERY_HEADER_SIZE 5 /*< 3 bytes size, 1 sequence, 1 command */
#define MAX_QUERYBUF_SIZE 2048
typedef struct parsing_info_st
{
#if defined(SS_DEBUG)
skygw_chk_t pi_chk_top;
#endif
void* pi_handle; /*< parsing info object pointer */
char* pi_query_plain_str; /*< query as plain string */
void (*pi_done_fp)(void *); /*< clean-up function for parsing info */
QC_FIELD_INFO* field_infos;
size_t field_infos_len;
size_t field_infos_capacity;
QC_FUNCTION_INFO* function_infos;
size_t function_infos_len;
size_t function_infos_capacity;
GWBUF* preparable_stmt;
qc_parse_result_t result;
int32_t type_mask;
#if defined(SS_DEBUG)
skygw_chk_t pi_chk_tail;
#endif
} parsing_info_t;
/** /**
* Defines what a particular name should be mapped to. * Defines what a particular name should be mapped to.
*/ */
@ -119,9 +95,7 @@ static NAME_MAPPING function_name_mappings_oracle[] =
{ NULL, NULL } { NULL, NULL }
}; };
static NAME_MAPPING* function_name_mappings = function_name_mappings_default; static const char* map_function_name(NAME_MAPPING* function_name_mappings, const char* from)
static const char* map_function_name(const char* from)
{ {
NAME_MAPPING* map = function_name_mappings; NAME_MAPPING* map = function_name_mappings;
const char* to = NULL; const char* to = NULL;
@ -141,6 +115,31 @@ static const char* map_function_name(const char* from)
return to ? to : from; return to ? to : from;
} }
#define MYSQL_COM_QUERY_HEADER_SIZE 5 /*< 3 bytes size, 1 sequence, 1 command */
#define MAX_QUERYBUF_SIZE 2048
typedef struct parsing_info_st
{
#if defined(SS_DEBUG)
skygw_chk_t pi_chk_top;
#endif
void* pi_handle; /*< parsing info object pointer */
char* pi_query_plain_str; /*< query as plain string */
void (*pi_done_fp)(void *); /*< clean-up function for parsing info */
QC_FIELD_INFO* field_infos;
size_t field_infos_len;
size_t field_infos_capacity;
QC_FUNCTION_INFO* function_infos;
size_t function_infos_len;
size_t function_infos_capacity;
GWBUF* preparable_stmt;
qc_parse_result_t result;
int32_t type_mask;
NAME_MAPPING* function_name_mappings;
#if defined(SS_DEBUG)
skygw_chk_t pi_chk_tail;
#endif
} parsing_info_t;
#define QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (t<QUERY_TYPE_WRITE ? true : false) #define QTYPE_LESS_RESTRICTIVE_THAN_WRITE(t) (t<QUERY_TYPE_WRITE ? true : false)
static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str); static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str);
@ -175,9 +174,27 @@ inline void get_string_and_length(const char* cs, const char** s, size_t* length
} }
#endif #endif
static qc_sql_mode_t default_qc_sql_mode = QC_SQL_MODE_DEFAULT; static struct
static thread_local qc_sql_mode_t thread_qc_sql_mode = QC_SQL_MODE_DEFAULT; {
static pthread_mutex_t sql_mode_mutex = PTHREAD_MUTEX_INITIALIZER; qc_sql_mode_t sql_mode;
pthread_mutex_t sql_mode_mutex;
NAME_MAPPING* function_name_mappings;
} this_unit =
{
QC_SQL_MODE_DEFAULT,
PTHREAD_MUTEX_INITIALIZER,
function_name_mappings_default
};
static thread_local struct
{
qc_sql_mode_t sql_mode;
NAME_MAPPING* function_name_mappings;
} this_thread =
{
QC_SQL_MODE_DEFAULT,
function_name_mappings_default
};
/** /**
* Ensures that the query is parsed. If it is not already parsed, it * Ensures that the query is parsed. If it is not already parsed, it
@ -206,10 +223,10 @@ bool ensure_query_is_parsed(GWBUF* query)
ss_debug(int rv); ss_debug(int rv);
ss_debug(rv = )pthread_mutex_lock(&sql_mode_mutex); ss_debug(rv = )pthread_mutex_lock(&this_unit.sql_mode_mutex);
ss_dassert(rv == 0); ss_dassert(rv == 0);
if (thread_qc_sql_mode == QC_SQL_MODE_ORACLE) if (this_thread.sql_mode == QC_SQL_MODE_ORACLE)
{ {
global_system_variables.sql_mode |= MODE_ORACLE; global_system_variables.sql_mode |= MODE_ORACLE;
} }
@ -220,7 +237,7 @@ bool ensure_query_is_parsed(GWBUF* query)
parsed = parse_query(query); parsed = parse_query(query);
ss_debug(rv = )pthread_mutex_unlock(&sql_mode_mutex); ss_debug(rv = )pthread_mutex_unlock(&this_unit.sql_mode_mutex);
ss_dassert(rv == 0); ss_dassert(rv == 0);
if (!parsed) if (!parsed)
@ -1675,6 +1692,8 @@ static parsing_info_t* parsing_info_init(void (*donefun)(void *))
pi->pi_handle = mysql; pi->pi_handle = mysql;
pi->pi_done_fp = donefun; pi->pi_done_fp = donefun;
pi->result = QC_QUERY_INVALID; pi->result = QC_QUERY_INVALID;
ss_dassert(this_thread.function_name_mappings);
pi->function_name_mappings = this_thread.function_name_mappings;
retblock: retblock:
return pi; return pi;
@ -2207,7 +2226,7 @@ static void add_function_info(parsing_info_t* info,
{ {
ss_dassert(name); ss_dassert(name);
name = map_function_name(name); name = map_function_name(info->function_name_mappings, name);
QC_FUNCTION_INFO item = { (char*)name, usage }; QC_FUNCTION_INFO item = { (char*)name, usage };
@ -2972,8 +2991,8 @@ int32_t qc_mysql_setup(const char* zArgs)
if (strcmp(value, "MODE_ORACLE") == 0) if (strcmp(value, "MODE_ORACLE") == 0)
{ {
default_qc_sql_mode = QC_SQL_MODE_ORACLE; this_unit.sql_mode = QC_SQL_MODE_ORACLE;
function_name_mappings = function_name_mappings_oracle; this_unit.function_name_mappings = function_name_mappings_oracle;
} }
else else
{ {
@ -3016,6 +3035,10 @@ int32_t qc_mysql_process_init(void)
if (rc != 0) if (rc != 0)
{ {
this_thread.sql_mode = this_unit.sql_mode;
ss_dassert(this_unit.function_name_mappings);
this_thread.function_name_mappings = this_unit.function_name_mappings;
MXS_ERROR("mysql_library_init() failed. Error code: %d", rc); MXS_ERROR("mysql_library_init() failed. Error code: %d", rc);
} }
else else
@ -3038,7 +3061,9 @@ void qc_mysql_process_end(void)
int32_t qc_mysql_thread_init(void) int32_t qc_mysql_thread_init(void)
{ {
thread_qc_sql_mode = default_qc_sql_mode; this_thread.sql_mode = this_unit.sql_mode;
ss_dassert(this_unit.function_name_mappings);
this_thread.function_name_mappings = this_unit.function_name_mappings;
bool inited = (mysql_thread_init() == 0); bool inited = (mysql_thread_init() == 0);
@ -3057,7 +3082,7 @@ void qc_mysql_thread_end(void)
int32_t qc_mysql_get_sql_mode(qc_sql_mode_t* sql_mode) int32_t qc_mysql_get_sql_mode(qc_sql_mode_t* sql_mode)
{ {
*sql_mode = thread_qc_sql_mode; *sql_mode = this_thread.sql_mode;
return QC_RESULT_OK; return QC_RESULT_OK;
} }
@ -3065,12 +3090,19 @@ int32_t qc_mysql_set_sql_mode(qc_sql_mode_t sql_mode)
{ {
int32_t rv = QC_RESULT_OK; int32_t rv = QC_RESULT_OK;
if ((sql_mode == QC_SQL_MODE_DEFAULT) || (sql_mode == QC_SQL_MODE_ORACLE)) switch (sql_mode)
{
thread_qc_sql_mode = sql_mode;
}
else
{ {
case QC_SQL_MODE_DEFAULT:
this_thread.sql_mode = sql_mode;
this_thread.function_name_mappings = function_name_mappings_default;
break;
case QC_SQL_MODE_ORACLE:
this_thread.sql_mode = sql_mode;
this_thread.function_name_mappings = function_name_mappings_oracle;
break;
default:
rv = QC_RESULT_ERROR; rv = QC_RESULT_ERROR;
} }

View File

@ -63,6 +63,34 @@ typedef enum qc_parse_as
QC_PARSE_AS_103 // Parse as embedded lib does in 10.3 QC_PARSE_AS_103 // Parse as embedded lib does in 10.3
} qc_parse_as_t; } qc_parse_as_t;
/**
* Defines what a particular name should be mapped to.
*/
typedef struct qc_name_mapping
{
const char* from;
const char* to;
} QC_NAME_MAPPING;
static QC_NAME_MAPPING function_name_mappings_default[] =
{
{ NULL, NULL }
};
static QC_NAME_MAPPING function_name_mappings_103[] =
{
{ "now", "current_timestamp" },
{ NULL, NULL }
};
// NOTE: Duplicate the information from function_name_mappings_103 here.
static QC_NAME_MAPPING function_name_mappings_oracle[] =
{
{ "now", "current_timestamp" },
{ "nvl", "ifnull" },
{ NULL, NULL }
};
/** /**
* Contains information about a particular query. * Contains information about a particular query.
*/ */
@ -100,36 +128,9 @@ typedef struct qc_sqlite_info
size_t function_infos_capacity; // The capacity of the function_infos array. size_t function_infos_capacity; // The capacity of the function_infos array.
bool initializing; // Whether we are initializing sqlite3. bool initializing; // Whether we are initializing sqlite3.
qc_sql_mode_t sql_mode; // The current sql_mode. qc_sql_mode_t sql_mode; // The current sql_mode.
QC_NAME_MAPPING* function_name_mappings; // How function names should be mapped.
} QC_SQLITE_INFO; } QC_SQLITE_INFO;
/**
* Defines what a particular name should be mapped to.
*/
typedef struct qc_name_mapping
{
const char* from;
const char* to;
} QC_NAME_MAPPING;
static QC_NAME_MAPPING function_name_mappings_default[] =
{
{ NULL, NULL }
};
static QC_NAME_MAPPING function_name_mappings_103[] =
{
{ "now", "current_timestamp" },
{ NULL, NULL }
};
// NOTE: Duplicate the information from function_name_mappings_103 here.
static QC_NAME_MAPPING function_name_mappings_oracle[] =
{
{ "now", "current_timestamp" },
{ "nvl", "ifnull" },
{ NULL, NULL }
};
/** /**
* The state of qc_sqlite. * The state of qc_sqlite.
*/ */
@ -152,6 +153,7 @@ static thread_local struct
sqlite3* db; // Thread specific database handle. sqlite3* db; // Thread specific database handle.
qc_sql_mode_t sql_mode; // What sql_mode is used. qc_sql_mode_t sql_mode; // What sql_mode is used.
QC_SQLITE_INFO* info; // The information for the current statement being classified. QC_SQLITE_INFO* info; // The information for the current statement being classified.
QC_NAME_MAPPING* function_name_mappings; // How function names should be mapped.
} this_thread; } this_thread;
/** /**
@ -182,7 +184,7 @@ static bool is_sequence_related_field(QC_SQLITE_INFO* info,
const char* column); const char* column);
static bool is_sequence_related_function(QC_SQLITE_INFO* info, const char* func_name); static bool is_sequence_related_function(QC_SQLITE_INFO* info, const char* func_name);
static void log_invalid_data(GWBUF* query, const char* message); static void log_invalid_data(GWBUF* query, const char* message);
static const char* map_function_name(const char* name); static const char* map_function_name(QC_NAME_MAPPING* function_name_mappings, const char* name);
static bool parse_query(GWBUF* query, uint32_t collect); static bool parse_query(GWBUF* query, uint32_t collect);
static void parse_query_string(const char* query, size_t len); static void parse_query_string(const char* query, size_t len);
static bool query_is_parsed(GWBUF* query, uint32_t collect); static bool query_is_parsed(GWBUF* query, uint32_t collect);
@ -439,6 +441,7 @@ static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info, uint32_t collect)
info->function_infos_capacity = 0; info->function_infos_capacity = 0;
info->initializing = false; info->initializing = false;
info->sql_mode = this_thread.sql_mode; info->sql_mode = this_thread.sql_mode;
info->function_name_mappings = this_thread.function_name_mappings;
return info; return info;
} }
@ -747,9 +750,17 @@ static void log_invalid_data(GWBUF* query, const char* message)
} }
} }
static const char* map_function_name(const char* from) /**
* Map a function name to another.
*
* @param function_name_mappings The name mapping to use.
* @param from The function name to map.
*
* @param The mapped name, or @c from if the name is not mapped.
*/
static const char* map_function_name(QC_NAME_MAPPING* function_name_mappings, const char* from)
{ {
QC_NAME_MAPPING* map = this_unit.function_name_mappings; QC_NAME_MAPPING* map = function_name_mappings;
const char* to = NULL; const char* to = NULL;
while (!to && map->from) while (!to && map->from)
@ -926,7 +937,7 @@ static void update_function_info(QC_SQLITE_INFO* info,
return; return;
} }
name = map_function_name(name); name = map_function_name(info->function_name_mappings, name);
QC_FUNCTION_INFO item = { (char*)name, usage }; QC_FUNCTION_INFO item = { (char*)name, usage };
@ -3499,6 +3510,7 @@ static int32_t qc_sqlite_thread_init(void)
if (rc == SQLITE_OK) if (rc == SQLITE_OK)
{ {
this_thread.sql_mode = this_unit.sql_mode; this_thread.sql_mode = this_unit.sql_mode;
this_thread.function_name_mappings = this_unit.function_name_mappings;
this_thread.initialized = true; this_thread.initialized = true;
MXS_INFO("In-memory sqlite database successfully opened for thread %lu.", MXS_INFO("In-memory sqlite database successfully opened for thread %lu.",
@ -3979,12 +3991,26 @@ int32_t qc_sqlite_set_sql_mode(qc_sql_mode_t sql_mode)
{ {
int32_t rv = QC_RESULT_OK; int32_t rv = QC_RESULT_OK;
if ((sql_mode == QC_SQL_MODE_DEFAULT) || (sql_mode == QC_SQL_MODE_ORACLE)) switch (sql_mode)
{ {
case QC_SQL_MODE_DEFAULT:
this_thread.sql_mode = sql_mode; this_thread.sql_mode = sql_mode;
if (this_unit.parse_as == QC_PARSE_AS_103)
{
this_thread.function_name_mappings = function_name_mappings_103;
} }
else else
{ {
this_thread.function_name_mappings = function_name_mappings_default;
}
break;
case QC_SQL_MODE_ORACLE:
this_thread.sql_mode = sql_mode;
this_thread.function_name_mappings = function_name_mappings_oracle;
break;
default:
rv = QC_RESULT_ERROR; rv = QC_RESULT_ERROR;
} }