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