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 | |