diff --git a/include/maxscale/query_classifier.h b/include/maxscale/query_classifier.h index ac7b0f984..d05c97bbe 100644 --- a/include/maxscale/query_classifier.h +++ b/include/maxscale/query_classifier.h @@ -30,6 +30,16 @@ typedef enum qc_init_kind QC_INIT_BOTH = 0x03 } qc_init_kind_t; +/** + * qc_option_t defines options that affect the classification. + */ +enum qc_option_t +{ + QC_OPTION_STRING_ARG_AS_FIELD = (1 << 0), /*< Report a string argument to a function as a field. */ +}; + +const uint32_t QC_OPTION_MASK = QC_OPTION_STRING_ARG_AS_FIELD; + /** * qc_sql_mode_t specifies what should be assumed of the statements * that will be parsed. @@ -441,6 +451,22 @@ typedef struct query_classifier * @param info The info to be closed. */ void (* qc_info_close)(QC_STMT_INFO* info); + + /** + * Gets the options of the *calling* thread. + * + * @return Bit mask of values from qc_option_t. + */ + uint32_t (* qc_get_options)(); + + /** + * Sets the options for the *calling* thread. + * + * @param options Bits from qc_option_t. + * + * @return QC_RESULT_OK if @c options is valid, otherwise QC_RESULT_ERROR. + */ + int32_t (* qc_set_options)(uint32_t options); } QUERY_CLASSIFIER; /** @@ -952,4 +978,20 @@ json_t* qc_get_cache_stats_as_json(); */ const char* qc_result_to_string(qc_parse_result_t result); +/** + * Gets the options of the *calling* thread. + * + * @return Bit mask of values from qc_option_t. + */ +uint32_t qc_get_options(); + +/** + * Sets the options for the *calling* thread. + * + * @param options Bits from qc_option_t. + * + * @return true if the options were valid, false otherwise. + */ +bool qc_set_options(uint32_t options); + MXS_END_DECLS diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index ceb581800..ad2c1e2fd 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -195,6 +195,7 @@ static struct static thread_local struct { qc_sql_mode_t sql_mode; + uint32_t options; NAME_MAPPING* function_name_mappings; // The version information is not used; the embedded library parses according // to the version of the embedded library it has been linked with. However, we @@ -203,6 +204,7 @@ static thread_local struct } this_thread = { QC_SQL_MODE_DEFAULT, + 0, function_name_mappings_default, 0 }; @@ -2475,6 +2477,19 @@ static void add_function_field_usage(st_select_lex* select, add_function_field_usage(select, static_cast(item), fi); break; + case Item::STRING_ITEM: + if (this_thread.options & QC_OPTION_STRING_ARG_AS_FIELD) + { + String* s = item->val_str(); + int len = s->length(); + char tmp[len + 1]; + memcpy(tmp, s->ptr(), len); + tmp[len] = 0; + + add_function_field_usage(nullptr, nullptr, tmp, fi); + } + break; + default: // mxb_assert(!true); ; @@ -3526,6 +3541,27 @@ int32_t qc_mysql_set_sql_mode(qc_sql_mode_t sql_mode) return rv; } +uint32_t qc_mysql_get_options() +{ + return this_thread.options; +} + +int32_t qc_mysql_set_options(uint32_t options) +{ + int32_t rv = QC_RESULT_OK; + + if ((options & ~QC_OPTION_MASK) == 0) + { + this_thread.options = options; + } + else + { + rv = QC_RESULT_ERROR; + } + + return rv; +} + /** * EXPORTS */ @@ -3561,6 +3597,8 @@ extern "C" qc_mysql_set_sql_mode, nullptr, // qc_info_dup not supported. nullptr, // qc_info_close not supported. + qc_mysql_get_options, + qc_mysql_set_options }; static MXS_MODULE info = diff --git a/query_classifier/qc_sqlite/qc_sqlite.cc b/query_classifier/qc_sqlite/qc_sqlite.cc index 0ae6e2023..f0f2e6323 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.cc +++ b/query_classifier/qc_sqlite/qc_sqlite.cc @@ -156,6 +156,7 @@ static thread_local struct bool initialized; // Whether the thread specific data has been initialized. sqlite3* pDb; // Thread specific database handle. qc_sql_mode_t sql_mode; // What sql_mode is used. + uint32_t options; // Options affecting classification. QcSqliteInfo* pInfo; // The information for the current statement being classified. uint64_t version; // Encoded version number uint32_t version_major; @@ -1141,6 +1142,13 @@ public: } } } + else if (pExpr->op == TK_STRING) + { + if (this_thread.options & QC_OPTION_STRING_ARG_AS_FIELD) + { + zColumn = pExpr->u.zToken; + } + } if (zColumn) { @@ -4541,6 +4549,8 @@ static int32_t qc_sqlite_get_sql_mode(qc_sql_mode_t* sql_mode); static int32_t qc_sqlite_set_sql_mode(qc_sql_mode_t sql_mode); static QC_STMT_INFO* qc_sqlite_info_dup(QC_STMT_INFO* info); static void qc_sqlite_info_close(QC_STMT_INFO* info); +static uint32_t qc_sqlite_get_options(); +static int32_t qc_sqlite_set_options(uint32_t options); static bool get_key_and_value(char* arg, const char** pkey, const char** pvalue) { @@ -5223,6 +5233,27 @@ void qc_sqlite_info_close(QC_STMT_INFO* info) static_cast(info)->dec_ref(); } +uint32_t qc_sqlite_get_options() +{ + return this_thread.options; +} + +int32_t qc_sqlite_set_options(uint32_t options) +{ + int32_t rv = QC_RESULT_OK; + + if ((options & ~QC_OPTION_MASK) == 0) + { + this_thread.options = options; + } + else + { + rv = QC_RESULT_ERROR; + } + + return rv; +} + /** * EXPORTS */ @@ -5258,6 +5289,8 @@ extern "C" qc_sqlite_set_sql_mode, qc_sqlite_info_dup, qc_sqlite_info_close, + qc_sqlite_get_options, + qc_sqlite_set_options }; static MXS_MODULE info = diff --git a/server/core/query_classifier.cc b/server/core/query_classifier.cc index 9208d31a9..cfb195591 100644 --- a/server/core/query_classifier.cc +++ b/server/core/query_classifier.cc @@ -96,9 +96,11 @@ class QCInfoCache; static thread_local struct { QCInfoCache* pInfo_cache; + uint32_t options; } this_thread = { - nullptr + nullptr, + 0 }; @@ -147,7 +149,8 @@ public: { const Entry& entry = i->second; - if (entry.sql_mode == this_unit.qc_sql_mode) + if ((entry.sql_mode == this_unit.qc_sql_mode) && + (entry.options == this_thread.options)) { mxb_assert(this_unit.classifier); this_unit.classifier->qc_info_dup(entry.pInfo); @@ -157,7 +160,7 @@ public: } else { - // If the sql_mode has changed, we discard the existing result. + // If the sql_mode or options has changed, we discard the existing result. erase(i); ++m_stats.misses; @@ -197,7 +200,7 @@ public: { this_unit.classifier->qc_info_dup(pInfo); - m_infos.emplace(canonical_stmt, Entry(pInfo, this_unit.qc_sql_mode)); + m_infos.emplace(canonical_stmt, Entry(pInfo, this_unit.qc_sql_mode, this_thread.options)); ++m_stats.inserts; m_stats.size += size; @@ -213,14 +216,16 @@ public: private: struct Entry { - Entry(QC_STMT_INFO* pInfo, qc_sql_mode_t sql_mode) + Entry(QC_STMT_INFO* pInfo, qc_sql_mode_t sql_mode, uint32_t options) : pInfo(pInfo) , sql_mode(sql_mode) + , options(options) { } QC_STMT_INFO* pInfo; qc_sql_mode_t sql_mode; + uint32_t options; }; typedef std::unordered_map InfosByStmt; @@ -1282,6 +1287,29 @@ void qc_set_sql_mode(qc_sql_mode_t sql_mode) } } +uint32_t qc_get_options() +{ + QC_TRACE(); + mxb_assert(this_unit.classifier); + + return this_unit.classifier->qc_get_options(); +} + +bool qc_set_options(uint32_t options) +{ + QC_TRACE(); + mxb_assert(this_unit.classifier); + + int32_t rv = this_unit.classifier->qc_set_options(options); + + if (rv == QC_RESULT_OK) + { + this_thread.options = options; + } + + return rv == QC_RESULT_OK; +} + void qc_get_cache_properties(QC_CACHE_PROPERTIES* properties) { properties->max_size = this_unit.cache_max_size();