diff --git a/mysql/changelog.d/21693.fixed b/mysql/changelog.d/21693.fixed new file mode 100644 index 0000000000000..6e0bd8ae4cf9a --- /dev/null +++ b/mysql/changelog.d/21693.fixed @@ -0,0 +1 @@ +Fixes error messages captured when failing to collect explain plans in order to surface actionable detail diff --git a/mysql/datadog_checks/mysql/statement_samples.py b/mysql/datadog_checks/mysql/statement_samples.py index b885a72e49501..60372c7c6198c 100644 --- a/mysql/datadog_checks/mysql/statement_samples.py +++ b/mysql/datadog_checks/mysql/statement_samples.py @@ -168,6 +168,9 @@ class DBExplainErrorCode(Enum): # database error i.e connection error database_error = 'database_error' + # failed to collect explain plan because the function is missing or invalid permissions + failed_function = 'failed_function' + # this could be the result of a missing EXPLAIN function invalid_schema = 'invalid_schema' @@ -301,10 +304,11 @@ def _use_schema(self, cursor, schema, explain_state_cache_key): except pymysql.err.DatabaseError as e: if len(e.args) != 2: raise + error_msg = f"{e.args[0]}: {e.args[1]}" if len(e.args) >= 2 else str(e) error_state = ExplainState( strategy=None, error_code=DBExplainErrorCode.use_schema_error, - error_message=str(type(e)), + error_message=error_msg, ) if e.args[0] in PYMYSQL_NON_RETRYABLE_ERRORS: self._collection_strategy_cache[explain_state_cache_key] = error_state @@ -729,9 +733,15 @@ def _explain_statement(self, cursor, statement, schema, obfuscated_statement, qu except pymysql.err.DatabaseError as e: if len(e.args) != 2: raise - error_state = ExplainState( - strategy=strategy, error_code=DBExplainErrorCode.database_error, error_message=str(type(e)) + mysql_error_code = e.args[0] if len(e.args) > 0 else None + error_msg = f"{e.args[0]}: {e.args[1]}" if len(e.args) >= 2 else str(e) + error_code = ( + DBExplainErrorCode.failed_function + if mysql_error_code + in (pymysql.constants.ER.TABLEACCESS_DENIED_ERROR, pymysql.constants.ER.PROCACCESS_DENIED_ERROR) + else DBExplainErrorCode.database_error ) + error_state = ExplainState(strategy=strategy, error_code=error_code, error_message=error_msg) error_states.append(error_state) self._log.debug( 'Failed to collect execution plan. error=%s, strategy=%s, schema=%s, statement="%s"', diff --git a/mysql/tests/test_statements.py b/mysql/tests/test_statements.py index d3c7f431d546b..3bd0cc7fe37c0 100644 --- a/mysql/tests/test_statements.py +++ b/mysql/tests/test_statements.py @@ -399,7 +399,13 @@ def run_query(q): ( 'information_schema', 'select * from testdb.users', - [{'strategy': 'PROCEDURE', 'code': 'database_error', 'message': ""}], + [ + { + 'strategy': 'PROCEDURE', + 'code': 'database_error', + 'message': "1044: Access denied for user 'dog'@'%' to database 'information_schema'", + } + ], StatementTruncationState.not_truncated.value, ), ( @@ -409,7 +415,7 @@ def run_query(q): { 'strategy': 'FQ_PROCEDURE', 'code': 'database_error', - 'message': "", + 'message': "1146: Table 'datadog.users' doesn't exist", } ], StatementTruncationState.not_truncated.value,