From 91e36ecaed51577144570816fc86f56c486dd8b1 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Mon, 31 Oct 2016 13:38:18 +0200 Subject: [PATCH] Make PREPARE operation explicit The operation of the statement to be prepared is no longer reported as the operation of the PREPARE statement. Instead, when the type of the statement is QUERY_TYPE_PREPARE_NAMED_STMT, the operation can be obtained using qc_get_prepare_operation(). The qc_mysqlembedded implementation will be provided in a subsequent commit. --- include/maxscale/query_classifier.h | 12 +++++++ .../qc_mysqlembedded/qc_mysqlembedded.cc | 21 +++++++++++ query_classifier/qc_sqlite/qc_sqlite.c | 35 +++++++++++++++++-- query_classifier/test/compare.cc | 28 +++++++++++++++ 4 files changed, 93 insertions(+), 3 deletions(-) diff --git a/include/maxscale/query_classifier.h b/include/maxscale/query_classifier.h index ccef8d2fc..b1b4a9f18 100644 --- a/include/maxscale/query_classifier.h +++ b/include/maxscale/query_classifier.h @@ -116,6 +116,7 @@ typedef struct query_classifier char* (*qc_get_affected_fields)(GWBUF* stmt); char** (*qc_get_database_names)(GWBUF* stmt, int* size); char* (*qc_get_prepare_name)(GWBUF* stmt); + qc_query_op_t (*qc_get_prepare_operation)(GWBUF* stmt); } QUERY_CLASSIFIER; /** @@ -285,6 +286,17 @@ qc_query_op_t qc_get_operation(GWBUF* stmt); */ char* qc_get_prepare_name(GWBUF* stmt); +/** + * Returns the operator of the prepared statement, if the statement + * is a PREPARE statement. + * + * @param stmt A buffer containing a COM_QUERY packet. + * + * @return The operator of the prepared statement, if the statement + * is a PREPARE statement; otherwise QUERY_OP_UNDEFINED. + */ +qc_query_op_t qc_get_prepare_operation(GWBUF* stmt); + /** * Returns the tables accessed by the statement. * diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index d8b45ca97..11848a89e 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -1929,6 +1929,26 @@ char* qc_get_prepare_name(GWBUF* stmt) return name; } +qc_query_op_t qc_get_prepare_operation(GWBUF* stmt) +{ + qc_query_op_t operation = QUERY_OP_UNDEFINED; + + if (stmt) + { + if (ensure_query_is_parsed(stmt)) + { + LEX* lex = get_lex(stmt); + + if (lex->sql_command == SQLCOM_PREPARE) + { + MXS_WARNING("qc_get_prepare_operation not implemented yet."); + } + } + } + + return operation; +} + namespace { @@ -2074,6 +2094,7 @@ static QUERY_CLASSIFIER qc = qc_get_affected_fields, qc_get_database_names, qc_get_prepare_name, + qc_get_prepare_operation, }; /* @see function load_module in load_utils.c for explanation of the following diff --git a/query_classifier/qc_sqlite/qc_sqlite.c b/query_classifier/qc_sqlite/qc_sqlite.c index bb180ac64..f78efebc8 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.c +++ b/query_classifier/qc_sqlite/qc_sqlite.c @@ -79,6 +79,7 @@ typedef struct qc_sqlite_info int keyword_1; // The first encountered keyword. int keyword_2; // The second encountered keyword. char* prepare_name; // The name of a prepared statement. + qc_query_op_t prepare_operation; // The operation of a prepared statement. size_t preparable_stmt_offset; // The start of the preparable statement. size_t preparable_stmt_length; // The length of the preparable statement. } QC_SQLITE_INFO; @@ -331,6 +332,7 @@ static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info) info->keyword_1 = 0; // Sqlite3 starts numbering tokens from 1, so 0 means info->keyword_2 = 0; // that we have not seen a keyword. info->prepare_name = NULL; + info->prepare_operation = QUERY_OP_UNDEFINED; info->preparable_stmt_offset = 0; info->preparable_stmt_length = 0; @@ -482,9 +484,7 @@ static bool parse_query(GWBUF* query) this_thread.info->query = NULL; this_thread.info->query_len = 0; - // TODO: Perhaps the rest of the stuff should be - // TODO: copied as well. - info->operation = preparable_info->operation; + info->prepare_operation = preparable_info->operation; info_free(preparable_info); } @@ -2923,6 +2923,34 @@ static char* qc_sqlite_get_prepare_name(GWBUF* query) return name; } +static qc_query_op_t qc_sqlite_get_prepare_operation(GWBUF* query) +{ + QC_TRACE(); + ss_dassert(this_unit.initialized); + ss_dassert(this_thread.initialized); + + qc_query_op_t op = QUERY_OP_UNDEFINED; + QC_SQLITE_INFO* info = get_query_info(query); + + if (info) + { + if (qc_info_is_valid(info->status)) + { + op = info->prepare_operation; + } + else if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO)) + { + log_invalid_data(query, "cannot report the operation of a prepared statement"); + } + } + else + { + MXS_ERROR("The query could not be parsed. Response not valid."); + } + + return op; +} + /** * EXPORTS */ @@ -2947,6 +2975,7 @@ static QUERY_CLASSIFIER qc = qc_sqlite_get_affected_fields, qc_sqlite_get_database_names, qc_sqlite_get_prepare_name, + qc_sqlite_get_prepare_operation, }; diff --git a/query_classifier/test/compare.cc b/query_classifier/test/compare.cc index 066f086ba..99fd1f7ba 100644 --- a/query_classifier/test/compare.cc +++ b/query_classifier/test/compare.cc @@ -844,6 +844,33 @@ bool compare_get_prepare_name(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, return success; } +bool compare_get_prepare_operation(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, + QUERY_CLASSIFIER* pClassifier2, GWBUF* pCopy2) +{ + bool success = false; + const char HEADING[] = "qc_get_prepare_operation : "; + + qc_query_op_t rv1 = pClassifier1->qc_get_prepare_operation(pCopy1); + qc_query_op_t rv2 = pClassifier2->qc_get_prepare_operation(pCopy2); + + stringstream ss; + ss << HEADING; + + if (rv1 == rv2) + { + ss << "Ok : " << qc_op_to_string(rv1); + success = true; + } + else + { + ss << "ERR: " << qc_op_to_string(rv1) << " != " << qc_op_to_string(rv2); + } + + report(success, ss.str()); + + return success; +} + bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, const string& s) { GWBUF* pCopy1 = create_gwbuf(s); @@ -863,6 +890,7 @@ bool compare(QUERY_CLASSIFIER* pClassifier1, QUERY_CLASSIFIER* pClassifier2, con errors += !compare_get_affected_fields(pClassifier1, pCopy1, pClassifier2, pCopy2); errors += !compare_get_database_names(pClassifier1, pCopy1, pClassifier2, pCopy2); errors += !compare_get_prepare_name(pClassifier1, pCopy1, pClassifier2, pCopy2); + errors += !compare_get_prepare_operation(pClassifier1, pCopy1, pClassifier2, pCopy2); gwbuf_free(pCopy1); gwbuf_free(pCopy2);