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
43bool
44Sql_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
101bool
102Diagnostics_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
133bool
134Statement_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
166Item *
167Statement_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
208bool
209Condition_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
267Item *
268Condition_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
293Item *
294Condition_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