diff --git a/include/maxscale/query_classifier.h b/include/maxscale/query_classifier.h index 6471c8be6..ce1f15f50 100644 --- a/include/maxscale/query_classifier.h +++ b/include/maxscale/query_classifier.h @@ -342,6 +342,23 @@ typedef struct query_classifier * exhaustion or equivalent. */ int32_t (*qc_get_function_info)(GWBUF* stmt, const QC_FUNCTION_INFO** infos, uint32_t* n_infos); + + /** + * Return the preparable statement of a PREPARE statement. + * + * @param stmt A statement. + * @param preparable_stmt On return, the preparable statement (provided @c stmt is a + * PREPARE statement), if @c QC_RESULT_OK is returned. Otherwise + * NULL. + * + * @attention The returned GWBUF is the property of @c stmt and will be deleted when + * @c stmt is. If the preparable statement need to be retained beyond the + * lifetime of @c stmt, it must be cloned. + * + * @return QC_RESULT_OK, if the parsing was not aborted due to resource + * exhaustion or equivalent. + */ + int32_t (*qc_get_preparable_stmt)(GWBUF* stmt, GWBUF** preparable_stmt); } QUERY_CLASSIFIER; /** @@ -578,6 +595,22 @@ char* qc_get_prepare_name(GWBUF* stmt); */ qc_query_op_t qc_get_prepare_operation(GWBUF* stmt); +/** + * Returns the preparable statement of a PREPARE statment. Other query classifier + * functions can then be used on the returned statement to find out information + * about the preparable statement. The returned @c GWBUF should not be used for + * anything else but for obtaining information about the preparable statement. + * + * @param stmt A buffer containing a COM_QUERY packet. + * + * @return The preparable statement, if @stmt was a PREPARE statement, or + * NULL. + * + * @attention The returned @c GWBUF is the property of @c stmt and will be + * deleted along with it. + */ +GWBUF* qc_get_preparable_stmt(GWBUF* stmt); + /** * Returns the tables accessed by the statement. * diff --git a/query_classifier/qc_dummy/qc_dummy.cc b/query_classifier/qc_dummy/qc_dummy.cc index 6b21f58d0..26fd9f74b 100644 --- a/query_classifier/qc_dummy/qc_dummy.cc +++ b/query_classifier/qc_dummy/qc_dummy.cc @@ -96,7 +96,7 @@ int32_t qc_dummy_setup(const char* args) return QC_RESULT_OK; } -int qc_dummy_process_init(void) +int32_t qc_dummy_process_init(void) { return QC_RESULT_OK; } @@ -105,7 +105,7 @@ void qc_dummy_process_end(void) { } -int qc_dummy_thread_init(void) +int32_t qc_dummy_thread_init(void) { return QC_RESULT_OK; } @@ -114,6 +114,12 @@ void qc_dummy_thread_end(void) { } +int32_t qc_dummy_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt) +{ + *preparable_stmt = NULL; + return QC_RESULT_OK; +} + extern "C" { MXS_MODULE* MXS_CREATE_MODULE() @@ -138,6 +144,7 @@ extern "C" qc_dummy_get_prepare_operation, qc_dummy_get_field_info, qc_dummy_get_function_info, + qc_dummy_get_preparable_stmt, }; static MXS_MODULE info = diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index d42c04a00..d4ee168a2 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -1851,6 +1851,15 @@ int32_t qc_mysql_get_prepare_operation(GWBUF* stmt, int32_t* operation) return QC_RESULT_OK; } +int32_t qc_mysql_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt) +{ + *preparable_stmt = NULL; + + // TODO: Extract preparable stmt. + ss_dassert(!true); + return QC_RESULT_OK; +} + static bool should_exclude(const char* name, List* excludep) { bool exclude = false; diff --git a/query_classifier/qc_sqlite/qc_sqlite.c b/query_classifier/qc_sqlite/qc_sqlite.c index 521315872..4cef10af7 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.c +++ b/query_classifier/qc_sqlite/qc_sqlite.c @@ -2796,6 +2796,7 @@ static int32_t qc_sqlite_get_table_names(GWBUF* query, int32_t fullnames, char** static int32_t qc_sqlite_get_canonical(GWBUF* query, char** canonical); static int32_t qc_sqlite_query_has_clause(GWBUF* query, int32_t* has_clause); static int32_t qc_sqlite_get_database_names(GWBUF* query, char*** names, int* sizep); +static int32_t qc_sqlite_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt); static bool get_key_and_value(char* arg, const char** pkey, const char** pvalue) { @@ -3411,6 +3412,38 @@ int32_t qc_sqlite_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** infos return rv; } +int32_t qc_sqlite_get_preparable_stmt(GWBUF* stmt, GWBUF** preparable_stmt) +{ + QC_TRACE(); + int32_t rv = QC_RESULT_ERROR; + ss_dassert(this_unit.initialized); + ss_dassert(this_thread.initialized); + + *preparable_stmt = NULL; + + QC_SQLITE_INFO* info = get_query_info(stmt); + + if (info) + { + if (qc_info_is_valid(info->status)) + { + // TODO: Extract the preparable stmt. + ss_dassert(!true); + rv = QC_RESULT_OK; + } + else if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) + { + log_invalid_data(stmt, "cannot report field info"); + } + } + else + { + MXS_ERROR("The query could not be parsed. Response not valid."); + } + + return rv; +} + /** * EXPORTS */ @@ -3437,6 +3470,7 @@ MXS_MODULE* MXS_CREATE_MODULE() qc_sqlite_get_prepare_operation, qc_sqlite_get_field_info, qc_sqlite_get_function_info, + qc_sqlite_get_preparable_stmt, }; static MXS_MODULE info = diff --git a/server/core/query_classifier.c b/server/core/query_classifier.c index b87fc562e..b124136d0 100644 --- a/server/core/query_classifier.c +++ b/server/core/query_classifier.c @@ -283,6 +283,18 @@ char* qc_get_prepare_name(GWBUF* query) return name; } +GWBUF* qc_get_preparable_stmt(GWBUF* stmt) +{ + QC_TRACE(); + ss_dassert(classifier); + + GWBUF* preparable_stmt = NULL; + + classifier->qc_get_preparable_stmt(stmt, &preparable_stmt); + + return preparable_stmt; +} + struct type_name_info field_usage_to_type_name_info(qc_field_usage_t usage) { struct type_name_info info;