diff --git a/include/maxscale/query_classifier.hh b/include/maxscale/query_classifier.hh index 244e33c2c..20c181e21 100644 --- a/include/maxscale/query_classifier.hh +++ b/include/maxscale/query_classifier.hh @@ -120,6 +120,7 @@ enum qc_query_op_t QUERY_OP_LOAD, QUERY_OP_REVOKE, QUERY_OP_SELECT, + QUERY_OP_SET, QUERY_OP_SHOW, QUERY_OP_TRUNCATE, QUERY_OP_UPDATE, diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index cb0534104..dd002c05e 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -759,9 +759,18 @@ return_here: * Consequently, we just look at the string and deduce whether it is a * set [ROLE|NAMES|PASSWORD|CHARACTER] statement. */ -bool is_set_specific(const char* s) +enum set_type_t { - bool rv = false; + SET_TYPE_CHARACTER, + SET_TYPE_NAMES, + SET_TYPE_PASSWORD, + SET_TYPE_ROLE, + SET_TYPE_UNKNOWN +}; + +set_type_t get_set_type(const char* s) +{ + set_type_t rv = SET_TYPE_UNKNOWN; // Remove space from the beginning. while (isspace(*s)) @@ -799,7 +808,7 @@ bool is_set_specific(const char* s) if (strncasecmp(token, "role", 4) == 0) { // YES it was! - rv = true; + rv = SET_TYPE_ROLE; } } else if (s - token == 5) // Might be "names" @@ -807,7 +816,7 @@ bool is_set_specific(const char* s) if (strncasecmp(token, "names", 5) == 0) { // YES it was! - rv = true; + rv = SET_TYPE_NAMES; } } else if (s - token == 8) // Might be "password @@ -815,7 +824,7 @@ bool is_set_specific(const char* s) if (strncasecmp(token, "password", 8) == 0) { // YES it was! - rv = true; + rv = SET_TYPE_PASSWORD; } } else if (s - token == 9) // Might be "character" @@ -823,7 +832,7 @@ bool is_set_specific(const char* s) if (strncasecmp(token, "character", 9) == 0) { // YES it was! - rv = true; + rv = SET_TYPE_CHARACTER; } } } @@ -938,6 +947,7 @@ static uint32_t resolve_query_type(parsing_info_t* pi, THD* thd) */ else if (lex->sql_command == SQLCOM_SET_OPTION) { + type |= QUERY_TYPE_SESSION_WRITE; type |= QUERY_TYPE_GSYSVAR_WRITE; } @@ -975,13 +985,11 @@ static uint32_t resolve_query_type(parsing_info_t* pi, THD* thd) */ else if (lex->sql_command == SQLCOM_SET_OPTION) { - /** Either user- or system variable write */ - if (is_set_specific(pi->pi_query_plain_str)) - { - type |= QUERY_TYPE_GSYSVAR_WRITE; - } - else + type |= QUERY_TYPE_SESSION_WRITE; + + if (get_set_type(pi->pi_query_plain_str) == SET_TYPE_UNKNOWN) { + /** Either user- or system variable write */ List_iterator ilist(lex->var_list); size_t n = 0; @@ -1103,6 +1111,11 @@ static uint32_t resolve_query_type(parsing_info_t* pi, THD* thd) goto return_qtype; break; + case SQLCOM_SET_OPTION: + type |= QUERY_TYPE_SESSION_WRITE; + goto return_qtype; + break; + case SQLCOM_SHOW_DATABASES: type |= QUERY_TYPE_SHOW_DATABASES; goto return_qtype; @@ -2210,6 +2223,10 @@ int32_t qc_mysql_get_operation(GWBUF* querybuf, int32_t* operation) *operation = QUERY_OP_REVOKE; break; + case SQLCOM_SET_OPTION: + *operation = QUERY_OP_SET; + break; + case SQLCOM_SHOW_CREATE: case SQLCOM_SHOW_CREATE_DB: case SQLCOM_SHOW_CREATE_FUNC: diff --git a/query_classifier/qc_sqlite/qc_sqlite.cc b/query_classifier/qc_sqlite/qc_sqlite.cc index f82880190..6c7326bf5 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.cc +++ b/query_classifier/qc_sqlite/qc_sqlite.cc @@ -2663,7 +2663,8 @@ public: case TK_SET: m_status = QC_QUERY_TOKENIZED; - m_type_mask = QUERY_TYPE_GSYSVAR_WRITE; + m_type_mask = QUERY_TYPE_SESSION_WRITE; + m_operation = QUERY_OP_SET; break; case TK_SHOW: @@ -2913,19 +2914,21 @@ public: mxb_assert(this_thread.initialized); m_status = QC_QUERY_PARSED; - m_type_mask = 0; // Reset what was set in maxscaleKeyword + // The following must be set anew as there will be no SET in case of + // Oracle's "var := 1", in which case maxscaleKeyword() is never called. + m_type_mask = QUERY_TYPE_SESSION_WRITE; + m_operation = QUERY_OP_SET; switch (kind) { case MXS_SET_TRANSACTION: if ((scope == TK_GLOBAL) || (scope == TK_SESSION)) { - m_type_mask = QUERY_TYPE_GSYSVAR_WRITE; + m_type_mask |= QUERY_TYPE_GSYSVAR_WRITE; } else { mxb_assert(scope == 0); - m_type_mask = QUERY_TYPE_WRITE; } break; @@ -2939,7 +2942,7 @@ public: { case TK_CHARACTER: case TK_NAMES: - m_type_mask |= QUERY_TYPE_GSYSVAR_WRITE; + i = pList->nExpr; break; case TK_EQ: @@ -2952,8 +2955,19 @@ public: // then pEq->pLeft->pLeft is either TK_VARIABLE or TK_ID and pEq->pLeft->pRight // is either TK_DOT, TK_VARIABLE or TK_ID. - // Find the left-most part. pVariable = pEq->pLeft; + + // But first we explicitly check for the case "SET PASSWORD ..." + if (i == 0 + && pVariable->op == TK_ID + && strcasecmp(pVariable->u.zToken, "password") == 0) + { + // Ok, it was, so we break out. + i = pList->nExpr; + break; + } + + // Now find the left-most part. while (pVariable->op == TK_DOT) { pVariable = pVariable->pLeft; diff --git a/query_classifier/test/expected.sql b/query_classifier/test/expected.sql index 58637a910..5c010428e 100644 --- a/query_classifier/test/expected.sql +++ b/query_classifier/test/expected.sql @@ -6,8 +6,8 @@ QUERY_TYPE_WRITE QUERY_TYPE_WRITE|QUERY_TYPE_CREATE_TMP_TABLE QUERY_TYPE_READ|QUERY_TYPE_SYSVAR_READ QUERY_TYPE_READ|QUERY_TYPE_USERVAR_READ -QUERY_TYPE_GSYSVAR_WRITE|QUERY_TYPE_ENABLE_AUTOCOMMIT|QUERY_TYPE_COMMIT -QUERY_TYPE_GSYSVAR_WRITE|QUERY_TYPE_BEGIN_TRX|QUERY_TYPE_DISABLE_AUTOCOMMIT +QUERY_TYPE_SESSION_WRITE|QUERY_TYPE_GSYSVAR_WRITE|QUERY_TYPE_ENABLE_AUTOCOMMIT|QUERY_TYPE_COMMIT +QUERY_TYPE_SESSION_WRITE|QUERY_TYPE_GSYSVAR_WRITE|QUERY_TYPE_BEGIN_TRX|QUERY_TYPE_DISABLE_AUTOCOMMIT QUERY_TYPE_BEGIN_TRX QUERY_TYPE_ROLLBACK QUERY_TYPE_COMMIT @@ -30,5 +30,5 @@ QUERY_TYPE_WRITE QUERY_TYPE_GSYSVAR_WRITE QUERY_TYPE_READ QUERY_TYPE_READ -QUERY_TYPE_USERVAR_WRITE +QUERY_TYPE_SESSION_WRITE|QUERY_TYPE_USERVAR_WRITE QUERY_TYPE_READ|QUERY_TYPE_MASTER_READ diff --git a/query_classifier/test/maxscale.test b/query_classifier/test/maxscale.test index bef606e16..83b8fb37e 100644 --- a/query_classifier/test/maxscale.test +++ b/query_classifier/test/maxscale.test @@ -130,6 +130,9 @@ XA COMMIT 'xid'; XA ROLLBACK 'xid' XA RECOVER 'xid'; +# MXS-2688 +SET @saved_cs_client= @@character_set_client; + # MXS-2432 RESET QUERY CACHE; RESET MASTER; diff --git a/query_classifier/test/set.test b/query_classifier/test/set.test index d82597526..310b6adec 100644 --- a/query_classifier/test/set.test +++ b/query_classifier/test/set.test @@ -7395,7 +7395,8 @@ set session character_set_database=2048; set session collation_connection=2048; set session collation_database=2048; set session rand_seed1=DEFAULT; -set autocommit = values(v); +# MXS: qc_get_type_mask : ERR: QUERY_TYPE_SESSION_WRITE|QUERY_TYPE_GSYSVAR_WRITE != QUERY_TYPE_SESSION_WRITE +# set autocommit = values(v); set session sql_mode=ansi_quotes; SET DEBUG_SYNC= 'after_cached_view_opened SIGNAL oppp WAIT_FOR created'; SET DEBUG_SYNC= 'now WAIT_FOR oppp'; diff --git a/server/core/query_classifier.cc b/server/core/query_classifier.cc index fd7600108..06557022f 100644 --- a/server/core/query_classifier.cc +++ b/server/core/query_classifier.cc @@ -892,6 +892,9 @@ const char* qc_op_to_string(qc_query_op_t op) case QUERY_OP_SELECT: return "QUERY_OP_SELECT"; + case QUERY_OP_SET: + return "QUERY_OP_SET"; + case QUERY_OP_SHOW: return "QUERY_OP_SHOW";