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);