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 02110-1335 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
execute(THD * thd)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
set_value(THD * thd,Item ** value)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
aggregate(THD * thd,const Diagnostics_area * da)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 *
get_value(THD * thd,const Diagnostics_area * da)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
aggregate(THD * thd,const Diagnostics_area * da)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->fix_fields_if_needed(thd, &m_cond_number_expr))
221     DBUG_RETURN(true);
222 
223   cond_number= m_cond_number_expr->val_int();
224 
225   /*
226     Limit to the number of available conditions. Warning_info::warn_count()
227     is not used because it indicates the number of condition regardless of
228     @@max_error_count, which prevents conditions from being pushed, but not
229     counted.
230   */
231   if (cond_number < 1 || (ulonglong) cond_number > da->cond_count())
232   {
233     my_error(ER_DA_INVALID_CONDITION_NUMBER, MYF(0));
234     DBUG_RETURN(true);
235   }
236 
237   /* Advance to the requested condition. */
238   while (cond_number--)
239     cond= it_conds++;
240 
241   DBUG_ASSERT(cond);
242 
243   /* Evaluate the requested information in the context of the condition. */
244   while ((cond_info_item= it_items++))
245   {
246     if ((rv= evaluate(thd, cond_info_item, cond)))
247       break;
248   }
249 
250   DBUG_RETURN(rv);
251 }
252 
253 
254 /**
255   Create an UTF-8 string item to represent a condition item string.
256 
257   @remark The string might not have a associated charset. For example,
258           this can be the case if the server does not or fails to process
259           the error message file.
260 
261   @remark See "Design notes about Sql_condition::m_message_text." in sql_error.cc
262 
263   @return Pointer to an string item, NULL on failure.
264 */
265 
266 Item *
make_utf8_string_item(THD * thd,const String * str)267 Condition_information_item::make_utf8_string_item(THD *thd, const String *str)
268 {
269   /* Default is utf8 character set and utf8_general_ci collation. */
270   CHARSET_INFO *to_cs= &my_charset_utf8_general_ci;
271   /* If a charset was not set, assume that no conversion is needed. */
272   CHARSET_INFO *from_cs= str->charset() ? str->charset() : to_cs;
273   String tmp(str->ptr(), str->length(), from_cs);
274   /* If necessary, convert the string (ignoring errors), then copy it over. */
275   uint conv_errors;
276   return new (thd->mem_root) Item_string(thd, &tmp, to_cs, &conv_errors,
277                          DERIVATION_COERCIBLE, MY_REPERTOIRE_UNICODE30);
278 }
279 
280 
281 /**
282   Obtain the value of this condition information item in the context of
283   a given condition.
284 
285   @param thd  The current thread.
286   @param da   The diagnostics area.
287 
288   @retval Item representing the value.
289   @retval NULL on error.
290 */
291 
292 Item *
get_value(THD * thd,const Sql_condition * cond)293 Condition_information_item::get_value(THD *thd, const Sql_condition *cond)
294 {
295   String str;
296   Item *value= NULL;
297   DBUG_ENTER("Condition_information_item::get_value");
298 
299   switch (m_name)
300   {
301   case CLASS_ORIGIN:
302     value= make_utf8_string_item(thd, &(cond->m_class_origin));
303     break;
304   case SUBCLASS_ORIGIN:
305     value= make_utf8_string_item(thd, &(cond->m_subclass_origin));
306     break;
307   case CONSTRAINT_CATALOG:
308     value= make_utf8_string_item(thd, &(cond->m_constraint_catalog));
309     break;
310   case CONSTRAINT_SCHEMA:
311     value= make_utf8_string_item(thd, &(cond->m_constraint_schema));
312     break;
313   case CONSTRAINT_NAME:
314     value= make_utf8_string_item(thd, &(cond->m_constraint_name));
315     break;
316   case CATALOG_NAME:
317     value= make_utf8_string_item(thd, &(cond->m_catalog_name));
318     break;
319   case SCHEMA_NAME:
320     value= make_utf8_string_item(thd, &(cond->m_schema_name));
321     break;
322   case TABLE_NAME:
323     value= make_utf8_string_item(thd, &(cond->m_table_name));
324     break;
325   case COLUMN_NAME:
326     value= make_utf8_string_item(thd, &(cond->m_column_name));
327     break;
328   case CURSOR_NAME:
329     value= make_utf8_string_item(thd, &(cond->m_cursor_name));
330     break;
331   case MESSAGE_TEXT:
332     value= make_utf8_string_item(thd, &(cond->m_message_text));
333     break;
334   case MYSQL_ERRNO:
335     value= new (thd->mem_root) Item_uint(thd, cond->m_sql_errno);
336     break;
337   case RETURNED_SQLSTATE:
338     str.set_ascii(cond->get_sqlstate(), strlen(cond->get_sqlstate()));
339     value= make_utf8_string_item(thd, &str);
340     break;
341   }
342 
343   DBUG_RETURN(value);
344 }
345 
346