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