| 1 | /* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 of the License. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | |
| 12 | You should have received a copy of the GNU General Public License |
| 13 | along with this program; if not, write to the Free Software |
| 14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ |
| 15 | |
| 16 | #include "mariadb.h" |
| 17 | #include "sql_list.h" // Sql_alloc, List, List_iterator |
| 18 | #include "sql_cmd.h" // Sql_cmd |
| 19 | #include "sql_class.h" // Diagnostics_area |
| 20 | #include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics |
| 21 | |
| 22 | /** |
| 23 | Execute this GET DIAGNOSTICS statement. |
| 24 | |
| 25 | @param thd The current thread. |
| 26 | |
| 27 | @remark Errors or warnings occurring during the execution of the GET |
| 28 | DIAGNOSTICS statement should not affect the diagnostics area |
| 29 | of a previous statement as the diagnostics information there |
| 30 | would be wiped out. Thus, in order to preserve the contents |
| 31 | of the diagnostics area from which information is being |
| 32 | retrieved, the GET DIAGNOSTICS statement is executed under |
| 33 | a separate diagnostics area. If any errors or warnings occur |
| 34 | during the execution of the GET DIAGNOSTICS statement, these |
| 35 | error or warnings (conditions) are appended to the list of |
| 36 | the original diagnostics area. The only exception to this is |
| 37 | fatal errors, which must always cause the statement to fail. |
| 38 | |
| 39 | @retval false on success. |
| 40 | @retval true on error |
| 41 | */ |
| 42 | |
| 43 | bool |
| 44 | Sql_cmd_get_diagnostics::execute(THD *thd) |
| 45 | { |
| 46 | bool rv; |
| 47 | Diagnostics_area new_stmt_da(thd->query_id, false, true); |
| 48 | Diagnostics_area *save_stmt_da= thd->get_stmt_da(); |
| 49 | DBUG_ENTER("Sql_cmd_get_diagnostics::execute" ); |
| 50 | |
| 51 | /* Disable the unneeded read-only mode of the original DA. */ |
| 52 | save_stmt_da->set_warning_info_read_only(false); |
| 53 | |
| 54 | /* Set new diagnostics area, execute statement and restore. */ |
| 55 | thd->set_stmt_da(&new_stmt_da); |
| 56 | rv= m_info->aggregate(thd, save_stmt_da); |
| 57 | thd->set_stmt_da(save_stmt_da); |
| 58 | |
| 59 | /* Bail out early if statement succeeded. */ |
| 60 | if (! rv) |
| 61 | { |
| 62 | thd->get_stmt_da()->set_ok_status(0, 0, NULL); |
| 63 | DBUG_RETURN(false); |
| 64 | } |
| 65 | |
| 66 | /* Statement failed, retrieve the error information for propagation. */ |
| 67 | uint sql_errno= new_stmt_da.sql_errno(); |
| 68 | const char *message= new_stmt_da.message(); |
| 69 | const char *sqlstate= new_stmt_da.get_sqlstate(); |
| 70 | |
| 71 | /* In case of a fatal error, set it into the original DA.*/ |
| 72 | if (unlikely(thd->is_fatal_error)) |
| 73 | { |
| 74 | save_stmt_da->set_error_status(sql_errno, message, sqlstate, NULL); |
| 75 | DBUG_RETURN(true); |
| 76 | } |
| 77 | |
| 78 | /* Otherwise, just append the new error as a exception condition. */ |
| 79 | save_stmt_da->push_warning(thd, sql_errno, sqlstate, |
| 80 | Sql_condition::WARN_LEVEL_ERROR, |
| 81 | message); |
| 82 | |
| 83 | /* Appending might have failed. */ |
| 84 | if (unlikely(!(rv= thd->is_error()))) |
| 85 | thd->get_stmt_da()->set_ok_status(0, 0, NULL); |
| 86 | |
| 87 | DBUG_RETURN(rv); |
| 88 | } |
| 89 | |
| 90 | |
| 91 | /** |
| 92 | Set a value for this item. |
| 93 | |
| 94 | @param thd The current thread. |
| 95 | @param value The obtained value. |
| 96 | |
| 97 | @retval false on success. |
| 98 | @retval true on error. |
| 99 | */ |
| 100 | |
| 101 | bool |
| 102 | Diagnostics_information_item::set_value(THD *thd, Item **value) |
| 103 | { |
| 104 | bool rv; |
| 105 | Settable_routine_parameter *srp; |
| 106 | DBUG_ENTER("Diagnostics_information_item::set_value" ); |
| 107 | |
| 108 | /* Get a settable reference to the target. */ |
| 109 | srp= m_target->get_settable_routine_parameter(); |
| 110 | |
| 111 | DBUG_ASSERT(srp); |
| 112 | |
| 113 | /* GET DIAGNOSTICS is not allowed in prepared statements */ |
| 114 | DBUG_ASSERT(srp->get_item_param() == NULL); |
| 115 | |
| 116 | /* Set variable/parameter value. */ |
| 117 | rv= srp->set_value(thd, thd->spcont, value); |
| 118 | |
| 119 | DBUG_RETURN(rv); |
| 120 | } |
| 121 | |
| 122 | |
| 123 | /** |
| 124 | Obtain statement information in the context of a given diagnostics area. |
| 125 | |
| 126 | @param thd The current thread. |
| 127 | @param da The diagnostics area. |
| 128 | |
| 129 | @retval false on success. |
| 130 | @retval true on error |
| 131 | */ |
| 132 | |
| 133 | bool |
| 134 | Statement_information::aggregate(THD *thd, const Diagnostics_area *da) |
| 135 | { |
| 136 | bool rv= false; |
| 137 | Statement_information_item *stmt_info_item; |
| 138 | List_iterator<Statement_information_item> it(*m_items); |
| 139 | DBUG_ENTER("Statement_information::aggregate" ); |
| 140 | |
| 141 | /* |
| 142 | Each specified target gets the value of each given |
| 143 | information item obtained from the diagnostics area. |
| 144 | */ |
| 145 | while ((stmt_info_item= it++)) |
| 146 | { |
| 147 | if ((rv= evaluate(thd, stmt_info_item, da))) |
| 148 | break; |
| 149 | } |
| 150 | |
| 151 | DBUG_RETURN(rv); |
| 152 | } |
| 153 | |
| 154 | |
| 155 | /** |
| 156 | Obtain the value of this statement information item in the context of |
| 157 | a given diagnostics area. |
| 158 | |
| 159 | @param thd The current thread. |
| 160 | @param da The diagnostics area. |
| 161 | |
| 162 | @retval Item representing the value. |
| 163 | @retval NULL on error. |
| 164 | */ |
| 165 | |
| 166 | Item * |
| 167 | Statement_information_item::get_value(THD *thd, const Diagnostics_area *da) |
| 168 | { |
| 169 | Item *value= NULL; |
| 170 | DBUG_ENTER("Statement_information_item::get_value" ); |
| 171 | |
| 172 | switch (m_name) |
| 173 | { |
| 174 | /* |
| 175 | The number of condition areas that have information. That is, |
| 176 | the number of errors and warnings within the diagnostics area. |
| 177 | */ |
| 178 | case NUMBER: |
| 179 | { |
| 180 | ulong count= da->cond_count(); |
| 181 | value= new (thd->mem_root) Item_uint(thd, count); |
| 182 | break; |
| 183 | } |
| 184 | /* |
| 185 | Number that shows how many rows were directly affected by |
| 186 | a data-change statement (INSERT, UPDATE, DELETE, MERGE, |
| 187 | REPLACE, LOAD). |
| 188 | */ |
| 189 | case ROW_COUNT: |
| 190 | value= new (thd->mem_root) Item_int(thd, thd->get_row_count_func()); |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | DBUG_RETURN(value); |
| 195 | } |
| 196 | |
| 197 | |
| 198 | /** |
| 199 | Obtain condition information in the context of a given diagnostics area. |
| 200 | |
| 201 | @param thd The current thread. |
| 202 | @param da The diagnostics area. |
| 203 | |
| 204 | @retval false on success. |
| 205 | @retval true on error |
| 206 | */ |
| 207 | |
| 208 | bool |
| 209 | Condition_information::aggregate(THD *thd, const Diagnostics_area *da) |
| 210 | { |
| 211 | bool rv= false; |
| 212 | longlong cond_number; |
| 213 | const Sql_condition *cond= NULL; |
| 214 | Condition_information_item *cond_info_item; |
| 215 | Diagnostics_area::Sql_condition_iterator it_conds= da->sql_conditions(); |
| 216 | List_iterator_fast<Condition_information_item> it_items(*m_items); |
| 217 | DBUG_ENTER("Condition_information::aggregate" ); |
| 218 | |
| 219 | /* Prepare the expression for evaluation. */ |
| 220 | if (!m_cond_number_expr->fixed && |
| 221 | m_cond_number_expr->fix_fields(thd, &m_cond_number_expr)) |
| 222 | DBUG_RETURN(true); |
| 223 | |
| 224 | cond_number= m_cond_number_expr->val_int(); |
| 225 | |
| 226 | /* |
| 227 | Limit to the number of available conditions. Warning_info::warn_count() |
| 228 | is not used because it indicates the number of condition regardless of |
| 229 | @@max_error_count, which prevents conditions from being pushed, but not |
| 230 | counted. |
| 231 | */ |
| 232 | if (cond_number < 1 || (ulonglong) cond_number > da->cond_count()) |
| 233 | { |
| 234 | my_error(ER_DA_INVALID_CONDITION_NUMBER, MYF(0)); |
| 235 | DBUG_RETURN(true); |
| 236 | } |
| 237 | |
| 238 | /* Advance to the requested condition. */ |
| 239 | while (cond_number--) |
| 240 | cond= it_conds++; |
| 241 | |
| 242 | DBUG_ASSERT(cond); |
| 243 | |
| 244 | /* Evaluate the requested information in the context of the condition. */ |
| 245 | while ((cond_info_item= it_items++)) |
| 246 | { |
| 247 | if ((rv= evaluate(thd, cond_info_item, cond))) |
| 248 | break; |
| 249 | } |
| 250 | |
| 251 | DBUG_RETURN(rv); |
| 252 | } |
| 253 | |
| 254 | |
| 255 | /** |
| 256 | Create an UTF-8 string item to represent a condition item string. |
| 257 | |
| 258 | @remark The string might not have a associated charset. For example, |
| 259 | this can be the case if the server does not or fails to process |
| 260 | the error message file. |
| 261 | |
| 262 | @remark See "Design notes about Sql_condition::m_message_text." in sql_error.cc |
| 263 | |
| 264 | @return Pointer to an string item, NULL on failure. |
| 265 | */ |
| 266 | |
| 267 | Item * |
| 268 | Condition_information_item::make_utf8_string_item(THD *thd, const String *str) |
| 269 | { |
| 270 | /* Default is utf8 character set and utf8_general_ci collation. */ |
| 271 | CHARSET_INFO *to_cs= &my_charset_utf8_general_ci; |
| 272 | /* If a charset was not set, assume that no conversion is needed. */ |
| 273 | CHARSET_INFO *from_cs= str->charset() ? str->charset() : to_cs; |
| 274 | String tmp(str->ptr(), str->length(), from_cs); |
| 275 | /* If necessary, convert the string (ignoring errors), then copy it over. */ |
| 276 | uint conv_errors; |
| 277 | return new (thd->mem_root) Item_string(thd, &tmp, to_cs, &conv_errors, |
| 278 | DERIVATION_COERCIBLE, MY_REPERTOIRE_UNICODE30); |
| 279 | } |
| 280 | |
| 281 | |
| 282 | /** |
| 283 | Obtain the value of this condition information item in the context of |
| 284 | a given condition. |
| 285 | |
| 286 | @param thd The current thread. |
| 287 | @param da The diagnostics area. |
| 288 | |
| 289 | @retval Item representing the value. |
| 290 | @retval NULL on error. |
| 291 | */ |
| 292 | |
| 293 | Item * |
| 294 | Condition_information_item::get_value(THD *thd, const Sql_condition *cond) |
| 295 | { |
| 296 | String str; |
| 297 | Item *value= NULL; |
| 298 | DBUG_ENTER("Condition_information_item::get_value" ); |
| 299 | |
| 300 | switch (m_name) |
| 301 | { |
| 302 | case CLASS_ORIGIN: |
| 303 | value= make_utf8_string_item(thd, &(cond->m_class_origin)); |
| 304 | break; |
| 305 | case SUBCLASS_ORIGIN: |
| 306 | value= make_utf8_string_item(thd, &(cond->m_subclass_origin)); |
| 307 | break; |
| 308 | case CONSTRAINT_CATALOG: |
| 309 | value= make_utf8_string_item(thd, &(cond->m_constraint_catalog)); |
| 310 | break; |
| 311 | case CONSTRAINT_SCHEMA: |
| 312 | value= make_utf8_string_item(thd, &(cond->m_constraint_schema)); |
| 313 | break; |
| 314 | case CONSTRAINT_NAME: |
| 315 | value= make_utf8_string_item(thd, &(cond->m_constraint_name)); |
| 316 | break; |
| 317 | case CATALOG_NAME: |
| 318 | value= make_utf8_string_item(thd, &(cond->m_catalog_name)); |
| 319 | break; |
| 320 | case SCHEMA_NAME: |
| 321 | value= make_utf8_string_item(thd, &(cond->m_schema_name)); |
| 322 | break; |
| 323 | case TABLE_NAME: |
| 324 | value= make_utf8_string_item(thd, &(cond->m_table_name)); |
| 325 | break; |
| 326 | case COLUMN_NAME: |
| 327 | value= make_utf8_string_item(thd, &(cond->m_column_name)); |
| 328 | break; |
| 329 | case CURSOR_NAME: |
| 330 | value= make_utf8_string_item(thd, &(cond->m_cursor_name)); |
| 331 | break; |
| 332 | case MESSAGE_TEXT: |
| 333 | value= make_utf8_string_item(thd, &(cond->m_message_text)); |
| 334 | break; |
| 335 | case MYSQL_ERRNO: |
| 336 | value= new (thd->mem_root) Item_uint(thd, cond->m_sql_errno); |
| 337 | break; |
| 338 | case RETURNED_SQLSTATE: |
| 339 | str.set_ascii(cond->get_sqlstate(), strlen(cond->get_sqlstate())); |
| 340 | value= make_utf8_string_item(thd, &str); |
| 341 | break; |
| 342 | } |
| 343 | |
| 344 | DBUG_RETURN(value); |
| 345 | } |
| 346 | |
| 347 | |