1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, 2018, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /**************************************************//** |
21 | @file que/que0que.cc |
22 | Query graph |
23 | |
24 | Created 5/27/1996 Heikki Tuuri |
25 | *******************************************************/ |
26 | |
27 | #include "ha_prototypes.h" |
28 | |
29 | #include "que0que.h" |
30 | #include "trx0trx.h" |
31 | #include "trx0roll.h" |
32 | #include "row0undo.h" |
33 | #include "row0ins.h" |
34 | #include "row0upd.h" |
35 | #include "row0sel.h" |
36 | #include "row0purge.h" |
37 | #include "dict0crea.h" |
38 | #include "log0log.h" |
39 | #include "eval0proc.h" |
40 | #include "lock0lock.h" |
41 | #include "eval0eval.h" |
42 | #include "pars0types.h" |
43 | |
44 | #define QUE_MAX_LOOPS_WITHOUT_CHECK 16 |
45 | |
46 | /* Short introduction to query graphs |
47 | ================================== |
48 | |
49 | A query graph consists of nodes linked to each other in various ways. The |
50 | execution starts at que_run_threads() which takes a que_thr_t parameter. |
51 | que_thr_t contains two fields that control query graph execution: run_node |
52 | and prev_node. run_node is the next node to execute and prev_node is the |
53 | last node executed. |
54 | |
55 | Each node has a pointer to a 'next' statement, i.e., its brother, and a |
56 | pointer to its parent node. The next pointer is NULL in the last statement |
57 | of a block. |
58 | |
59 | Loop nodes contain a link to the first statement of the enclosed statement |
60 | list. While the loop runs, que_thr_step() checks if execution to the loop |
61 | node came from its parent or from one of the statement nodes in the loop. If |
62 | it came from the parent of the loop node it starts executing the first |
63 | statement node in the loop. If it came from one of the statement nodes in |
64 | the loop, then it checks if the statement node has another statement node |
65 | following it, and runs it if so. |
66 | |
67 | To signify loop ending, the loop statements (see e.g. while_step()) set |
68 | que_thr_t->run_node to the loop node's parent node. This is noticed on the |
69 | next call of que_thr_step() and execution proceeds to the node pointed to by |
70 | the loop node's 'next' pointer. |
71 | |
72 | For example, the code: |
73 | |
74 | X := 1; |
75 | WHILE X < 5 LOOP |
76 | X := X + 1; |
77 | X := X + 1; |
78 | X := 5 |
79 | |
80 | will result in the following node hierarchy, with the X-axis indicating |
81 | 'next' links and the Y-axis indicating parent/child links: |
82 | |
83 | A - W - A |
84 | | |
85 | | |
86 | A - A |
87 | |
88 | A = assign_node_t, W = while_node_t. */ |
89 | |
90 | /* How a stored procedure containing COMMIT or ROLLBACK commands |
91 | is executed? |
92 | |
93 | The commit or rollback can be seen as a subprocedure call. |
94 | |
95 | When the transaction starts to handle a rollback or commit. |
96 | It builds a query graph which, when executed, will roll back |
97 | or commit the incomplete transaction. The transaction |
98 | is moved to the TRX_QUE_ROLLING_BACK or TRX_QUE_COMMITTING state. |
99 | If specified, the SQL cursors opened by the transaction are closed. |
100 | When the execution of the graph completes, it is like returning |
101 | from a subprocedure: the query thread which requested the operation |
102 | starts running again. */ |
103 | |
104 | /**********************************************************************//** |
105 | Moves a thread from another state to the QUE_THR_RUNNING state. Increments |
106 | the n_active_thrs counters of the query graph and transaction. |
107 | ***NOTE***: This is the only function in which such a transition is allowed |
108 | to happen! */ |
109 | static |
110 | void |
111 | que_thr_move_to_run_state( |
112 | /*======================*/ |
113 | que_thr_t* thr); /*!< in: an query thread */ |
114 | |
115 | /***********************************************************************//** |
116 | Creates a query graph fork node. |
117 | @return own: fork node */ |
118 | que_fork_t* |
119 | que_fork_create( |
120 | /*============*/ |
121 | que_t* graph, /*!< in: graph, if NULL then this |
122 | fork node is assumed to be the |
123 | graph root */ |
124 | que_node_t* parent, /*!< in: parent node */ |
125 | ulint fork_type, /*!< in: fork type */ |
126 | mem_heap_t* heap) /*!< in: memory heap where created */ |
127 | { |
128 | que_fork_t* fork; |
129 | |
130 | ut_ad(heap); |
131 | |
132 | fork = static_cast<que_fork_t*>(mem_heap_zalloc(heap, sizeof(*fork))); |
133 | |
134 | fork->heap = heap; |
135 | |
136 | fork->fork_type = fork_type; |
137 | |
138 | fork->common.parent = parent; |
139 | |
140 | fork->common.type = QUE_NODE_FORK; |
141 | |
142 | fork->state = QUE_FORK_COMMAND_WAIT; |
143 | |
144 | fork->graph = (graph != NULL) ? graph : fork; |
145 | |
146 | UT_LIST_INIT(fork->thrs, &que_thr_t::thrs); |
147 | |
148 | return(fork); |
149 | } |
150 | |
151 | |
152 | /** Creates a query graph thread node. |
153 | @param[in] parent parent node, i.e., a fork node |
154 | @param[in] heap memory heap where created |
155 | @param[in] prebuilt row prebuilt structure |
156 | @return own: query thread node */ |
157 | que_thr_t* |
158 | que_thr_create( |
159 | que_fork_t* parent, |
160 | mem_heap_t* heap, |
161 | row_prebuilt_t* prebuilt) |
162 | { |
163 | que_thr_t* thr; |
164 | |
165 | ut_ad(parent != NULL); |
166 | ut_ad(heap != NULL); |
167 | |
168 | thr = static_cast<que_thr_t*>(mem_heap_zalloc(heap, sizeof(*thr))); |
169 | |
170 | thr->graph = parent->graph; |
171 | |
172 | thr->common.parent = parent; |
173 | |
174 | thr->magic_n = QUE_THR_MAGIC_N; |
175 | |
176 | thr->common.type = QUE_NODE_THR; |
177 | |
178 | thr->state = QUE_THR_COMMAND_WAIT; |
179 | |
180 | thr->lock_state = QUE_THR_LOCK_NOLOCK; |
181 | |
182 | thr->prebuilt = prebuilt; |
183 | |
184 | UT_LIST_ADD_LAST(parent->thrs, thr); |
185 | |
186 | return(thr); |
187 | } |
188 | |
189 | /**********************************************************************//** |
190 | Moves a suspended query thread to the QUE_THR_RUNNING state and may release |
191 | a worker thread to execute it. This function should be used to end |
192 | the wait state of a query thread waiting for a lock or a stored procedure |
193 | completion. |
194 | @return the query thread that needs to be released. */ |
195 | que_thr_t* |
196 | que_thr_end_lock_wait( |
197 | /*==================*/ |
198 | trx_t* trx) /*!< in: transaction with que_state in |
199 | QUE_THR_LOCK_WAIT */ |
200 | { |
201 | que_thr_t* thr; |
202 | ibool was_active; |
203 | |
204 | ut_ad(lock_mutex_own()); |
205 | ut_ad(trx_mutex_own(trx)); |
206 | |
207 | thr = trx->lock.wait_thr; |
208 | |
209 | ut_ad(thr != NULL); |
210 | |
211 | ut_ad(trx->lock.que_state == TRX_QUE_LOCK_WAIT); |
212 | /* In MySQL this is the only possible state here */ |
213 | ut_a(thr->state == QUE_THR_LOCK_WAIT); |
214 | |
215 | was_active = thr->is_active; |
216 | |
217 | que_thr_move_to_run_state(thr); |
218 | |
219 | trx->lock.que_state = TRX_QUE_RUNNING; |
220 | |
221 | trx->lock.wait_thr = NULL; |
222 | |
223 | /* In MySQL we let the OS thread (not just the query thread) to wait |
224 | for the lock to be released: */ |
225 | |
226 | return((!was_active && thr != NULL) ? thr : NULL); |
227 | } |
228 | |
229 | /**********************************************************************//** |
230 | Inits a query thread for a command. */ |
231 | UNIV_INLINE |
232 | void |
233 | que_thr_init_command( |
234 | /*=================*/ |
235 | que_thr_t* thr) /*!< in: query thread */ |
236 | { |
237 | thr->run_node = thr; |
238 | thr->prev_node = thr->common.parent; |
239 | |
240 | que_thr_move_to_run_state(thr); |
241 | } |
242 | |
243 | /**********************************************************************//** |
244 | Round robin scheduler. |
245 | @return a query thread of the graph moved to QUE_THR_RUNNING state, or |
246 | NULL; the query thread should be executed by que_run_threads by the |
247 | caller */ |
248 | que_thr_t* |
249 | que_fork_scheduler_round_robin( |
250 | /*===========================*/ |
251 | que_fork_t* fork, /*!< in: a query fork */ |
252 | que_thr_t* thr) /*!< in: current pos */ |
253 | { |
254 | trx_mutex_enter(fork->trx); |
255 | |
256 | /* If no current, start first available. */ |
257 | if (thr == NULL) { |
258 | thr = UT_LIST_GET_FIRST(fork->thrs); |
259 | } else { |
260 | thr = UT_LIST_GET_NEXT(thrs, thr); |
261 | } |
262 | |
263 | if (thr) { |
264 | |
265 | fork->state = QUE_FORK_ACTIVE; |
266 | |
267 | fork->last_sel_node = NULL; |
268 | |
269 | switch (thr->state) { |
270 | case QUE_THR_COMMAND_WAIT: |
271 | case QUE_THR_COMPLETED: |
272 | ut_a(!thr->is_active); |
273 | que_thr_init_command(thr); |
274 | break; |
275 | |
276 | case QUE_THR_SUSPENDED: |
277 | case QUE_THR_LOCK_WAIT: |
278 | default: |
279 | ut_error; |
280 | |
281 | } |
282 | } |
283 | |
284 | trx_mutex_exit(fork->trx); |
285 | |
286 | return(thr); |
287 | } |
288 | |
289 | /**********************************************************************//** |
290 | Starts execution of a command in a query fork. Picks a query thread which |
291 | is not in the QUE_THR_RUNNING state and moves it to that state. If none |
292 | can be chosen, a situation which may arise in parallelized fetches, NULL |
293 | is returned. |
294 | @return a query thread of the graph moved to QUE_THR_RUNNING state, or |
295 | NULL; the query thread should be executed by que_run_threads by the |
296 | caller */ |
297 | que_thr_t* |
298 | que_fork_start_command( |
299 | /*===================*/ |
300 | que_fork_t* fork) /*!< in: a query fork */ |
301 | { |
302 | que_thr_t* thr; |
303 | que_thr_t* suspended_thr = NULL; |
304 | que_thr_t* completed_thr = NULL; |
305 | |
306 | fork->state = QUE_FORK_ACTIVE; |
307 | |
308 | fork->last_sel_node = NULL; |
309 | |
310 | suspended_thr = NULL; |
311 | completed_thr = NULL; |
312 | |
313 | /* Choose the query thread to run: usually there is just one thread, |
314 | but in a parallelized select, which necessarily is non-scrollable, |
315 | there may be several to choose from */ |
316 | |
317 | /* First we try to find a query thread in the QUE_THR_COMMAND_WAIT |
318 | state. Then we try to find a query thread in the QUE_THR_SUSPENDED |
319 | state, finally we try to find a query thread in the QUE_THR_COMPLETED |
320 | state */ |
321 | |
322 | /* We make a single pass over the thr list within which we note which |
323 | threads are ready to run. */ |
324 | for (thr = UT_LIST_GET_FIRST(fork->thrs); |
325 | thr != NULL; |
326 | thr = UT_LIST_GET_NEXT(thrs, thr)) { |
327 | |
328 | switch (thr->state) { |
329 | case QUE_THR_COMMAND_WAIT: |
330 | |
331 | /* We have to send the initial message to query thread |
332 | to start it */ |
333 | |
334 | que_thr_init_command(thr); |
335 | |
336 | return(thr); |
337 | |
338 | case QUE_THR_SUSPENDED: |
339 | /* In this case the execution of the thread was |
340 | suspended: no initial message is needed because |
341 | execution can continue from where it was left */ |
342 | if (!suspended_thr) { |
343 | suspended_thr = thr; |
344 | } |
345 | |
346 | break; |
347 | |
348 | case QUE_THR_COMPLETED: |
349 | if (!completed_thr) { |
350 | completed_thr = thr; |
351 | } |
352 | |
353 | break; |
354 | |
355 | case QUE_THR_RUNNING: |
356 | case QUE_THR_LOCK_WAIT: |
357 | case QUE_THR_PROCEDURE_WAIT: |
358 | ut_error; |
359 | } |
360 | } |
361 | |
362 | if (suspended_thr) { |
363 | |
364 | thr = suspended_thr; |
365 | que_thr_move_to_run_state(thr); |
366 | |
367 | } else if (completed_thr) { |
368 | |
369 | thr = completed_thr; |
370 | que_thr_init_command(thr); |
371 | } else { |
372 | ut_error; |
373 | } |
374 | |
375 | return(thr); |
376 | } |
377 | |
378 | /**********************************************************************//** |
379 | Calls que_graph_free_recursive for statements in a statement list. */ |
380 | static |
381 | void |
382 | que_graph_free_stat_list( |
383 | /*=====================*/ |
384 | que_node_t* node) /*!< in: first query graph node in the list */ |
385 | { |
386 | while (node) { |
387 | que_graph_free_recursive(node); |
388 | |
389 | node = que_node_get_next(node); |
390 | } |
391 | } |
392 | |
393 | /**********************************************************************//** |
394 | Frees a query graph, but not the heap where it was created. Does not free |
395 | explicit cursor declarations, they are freed in que_graph_free. */ |
396 | void |
397 | que_graph_free_recursive( |
398 | /*=====================*/ |
399 | que_node_t* node) /*!< in: query graph node */ |
400 | { |
401 | que_fork_t* fork; |
402 | que_thr_t* thr; |
403 | undo_node_t* undo; |
404 | sel_node_t* sel; |
405 | ins_node_t* ins; |
406 | upd_node_t* upd; |
407 | tab_node_t* cre_tab; |
408 | ind_node_t* cre_ind; |
409 | purge_node_t* purge; |
410 | |
411 | DBUG_ENTER("que_graph_free_recursive" ); |
412 | |
413 | if (node == NULL) { |
414 | |
415 | DBUG_VOID_RETURN; |
416 | } |
417 | |
418 | DBUG_PRINT("que_graph_free_recursive" , |
419 | ("node: %p, type: " ULINTPF, node, |
420 | que_node_get_type(node))); |
421 | |
422 | switch (que_node_get_type(node)) { |
423 | |
424 | case QUE_NODE_FORK: |
425 | fork = static_cast<que_fork_t*>(node); |
426 | |
427 | thr = UT_LIST_GET_FIRST(fork->thrs); |
428 | |
429 | while (thr) { |
430 | que_graph_free_recursive(thr); |
431 | |
432 | thr = UT_LIST_GET_NEXT(thrs, thr); |
433 | } |
434 | |
435 | break; |
436 | case QUE_NODE_THR: |
437 | |
438 | thr = static_cast<que_thr_t*>(node); |
439 | |
440 | ut_a(thr->magic_n == QUE_THR_MAGIC_N); |
441 | |
442 | thr->magic_n = QUE_THR_MAGIC_FREED; |
443 | |
444 | que_graph_free_recursive(thr->child); |
445 | |
446 | break; |
447 | case QUE_NODE_UNDO: |
448 | |
449 | undo = static_cast<undo_node_t*>(node); |
450 | |
451 | mem_heap_free(undo->heap); |
452 | |
453 | break; |
454 | case QUE_NODE_SELECT: |
455 | |
456 | sel = static_cast<sel_node_t*>(node); |
457 | |
458 | sel_node_free_private(sel); |
459 | |
460 | break; |
461 | case QUE_NODE_INSERT: |
462 | |
463 | ins = static_cast<ins_node_t*>(node); |
464 | |
465 | que_graph_free_recursive(ins->select); |
466 | ins->select = NULL; |
467 | |
468 | if (ins->entry_sys_heap != NULL) { |
469 | mem_heap_free(ins->entry_sys_heap); |
470 | ins->entry_sys_heap = NULL; |
471 | } |
472 | |
473 | break; |
474 | case QUE_NODE_PURGE: |
475 | purge = static_cast<purge_node_t*>(node); |
476 | |
477 | mem_heap_free(purge->heap); |
478 | |
479 | break; |
480 | |
481 | case QUE_NODE_UPDATE: |
482 | upd = static_cast<upd_node_t*>(node); |
483 | |
484 | if (upd->in_mysql_interface) { |
485 | |
486 | btr_pcur_free_for_mysql(upd->pcur); |
487 | upd->in_mysql_interface = false; |
488 | } |
489 | |
490 | que_graph_free_recursive(upd->cascade_node); |
491 | |
492 | if (upd->cascade_heap) { |
493 | mem_heap_free(upd->cascade_heap); |
494 | upd->cascade_heap = NULL; |
495 | } |
496 | |
497 | que_graph_free_recursive(upd->select); |
498 | upd->select = NULL; |
499 | |
500 | if (upd->heap != NULL) { |
501 | mem_heap_free(upd->heap); |
502 | upd->heap = NULL; |
503 | } |
504 | |
505 | break; |
506 | case QUE_NODE_CREATE_TABLE: |
507 | cre_tab = static_cast<tab_node_t*>(node); |
508 | |
509 | que_graph_free_recursive(cre_tab->tab_def); |
510 | que_graph_free_recursive(cre_tab->col_def); |
511 | que_graph_free_recursive(cre_tab->v_col_def); |
512 | |
513 | mem_heap_free(cre_tab->heap); |
514 | |
515 | break; |
516 | case QUE_NODE_CREATE_INDEX: |
517 | cre_ind = static_cast<ind_node_t*>(node); |
518 | |
519 | que_graph_free_recursive(cre_ind->ind_def); |
520 | que_graph_free_recursive(cre_ind->field_def); |
521 | |
522 | mem_heap_free(cre_ind->heap); |
523 | |
524 | break; |
525 | case QUE_NODE_PROC: |
526 | que_graph_free_stat_list(((proc_node_t*) node)->stat_list); |
527 | |
528 | break; |
529 | case QUE_NODE_IF: |
530 | que_graph_free_stat_list(((if_node_t*) node)->stat_list); |
531 | que_graph_free_stat_list(((if_node_t*) node)->else_part); |
532 | que_graph_free_stat_list(((if_node_t*) node)->elsif_list); |
533 | |
534 | break; |
535 | case QUE_NODE_ELSIF: |
536 | que_graph_free_stat_list(((elsif_node_t*) node)->stat_list); |
537 | |
538 | break; |
539 | case QUE_NODE_WHILE: |
540 | que_graph_free_stat_list(((while_node_t*) node)->stat_list); |
541 | |
542 | break; |
543 | case QUE_NODE_FOR: |
544 | que_graph_free_stat_list(((for_node_t*) node)->stat_list); |
545 | |
546 | break; |
547 | |
548 | case QUE_NODE_ASSIGNMENT: |
549 | case QUE_NODE_EXIT: |
550 | case QUE_NODE_RETURN: |
551 | case QUE_NODE_COMMIT: |
552 | case QUE_NODE_ROLLBACK: |
553 | case QUE_NODE_LOCK: |
554 | case QUE_NODE_FUNC: |
555 | case QUE_NODE_ORDER: |
556 | case QUE_NODE_ROW_PRINTF: |
557 | case QUE_NODE_OPEN: |
558 | case QUE_NODE_FETCH: |
559 | /* No need to do anything */ |
560 | |
561 | break; |
562 | default: |
563 | ut_error; |
564 | } |
565 | |
566 | DBUG_VOID_RETURN; |
567 | } |
568 | |
569 | /**********************************************************************//** |
570 | Frees a query graph. */ |
571 | void |
572 | que_graph_free( |
573 | /*===========*/ |
574 | que_t* graph) /*!< in: query graph; we assume that the memory |
575 | heap where this graph was created is private |
576 | to this graph: if not, then use |
577 | que_graph_free_recursive and free the heap |
578 | afterwards! */ |
579 | { |
580 | ut_ad(graph); |
581 | |
582 | if (graph->sym_tab) { |
583 | /* The following call frees dynamic memory allocated |
584 | for variables etc. during execution. Frees also explicit |
585 | cursor definitions. */ |
586 | |
587 | sym_tab_free_private(graph->sym_tab); |
588 | } |
589 | |
590 | if (graph->info && graph->info->graph_owns_us) { |
591 | pars_info_free(graph->info); |
592 | } |
593 | |
594 | que_graph_free_recursive(graph); |
595 | |
596 | mem_heap_free(graph->heap); |
597 | } |
598 | |
599 | /****************************************************************//** |
600 | Performs an execution step on a thr node. |
601 | @return query thread to run next, or NULL if none */ |
602 | static |
603 | que_thr_t* |
604 | que_thr_node_step( |
605 | /*==============*/ |
606 | que_thr_t* thr) /*!< in: query thread where run_node must |
607 | be the thread node itself */ |
608 | { |
609 | ut_ad(thr->run_node == thr); |
610 | |
611 | if (thr->prev_node == thr->common.parent) { |
612 | /* If control to the node came from above, it is just passed |
613 | on */ |
614 | |
615 | thr->run_node = thr->child; |
616 | |
617 | return(thr); |
618 | } |
619 | |
620 | trx_mutex_enter(thr_get_trx(thr)); |
621 | |
622 | if (que_thr_peek_stop(thr)) { |
623 | |
624 | trx_mutex_exit(thr_get_trx(thr)); |
625 | |
626 | return(thr); |
627 | } |
628 | |
629 | /* Thread execution completed */ |
630 | |
631 | thr->state = QUE_THR_COMPLETED; |
632 | |
633 | trx_mutex_exit(thr_get_trx(thr)); |
634 | |
635 | return(NULL); |
636 | } |
637 | |
638 | /**********************************************************************//** |
639 | Moves a thread from another state to the QUE_THR_RUNNING state. Increments |
640 | the n_active_thrs counters of the query graph and transaction if thr was |
641 | not active. |
642 | ***NOTE***: This and ..._mysql are the only functions in which such a |
643 | transition is allowed to happen! */ |
644 | static |
645 | void |
646 | que_thr_move_to_run_state( |
647 | /*======================*/ |
648 | que_thr_t* thr) /*!< in: an query thread */ |
649 | { |
650 | ut_ad(thr->state != QUE_THR_RUNNING); |
651 | |
652 | if (!thr->is_active) { |
653 | trx_t* trx; |
654 | |
655 | trx = thr_get_trx(thr); |
656 | |
657 | thr->graph->n_active_thrs++; |
658 | |
659 | trx->lock.n_active_thrs++; |
660 | |
661 | thr->is_active = TRUE; |
662 | } |
663 | |
664 | thr->state = QUE_THR_RUNNING; |
665 | } |
666 | |
667 | /**********************************************************************//** |
668 | Stops a query thread if graph or trx is in a state requiring it. The |
669 | conditions are tested in the order (1) graph, (2) trx. |
670 | @return TRUE if stopped */ |
671 | ibool |
672 | que_thr_stop( |
673 | /*=========*/ |
674 | que_thr_t* thr) /*!< in: query thread */ |
675 | { |
676 | que_t* graph; |
677 | trx_t* trx = thr_get_trx(thr); |
678 | |
679 | graph = thr->graph; |
680 | |
681 | ut_ad(trx_mutex_own(trx)); |
682 | |
683 | if (graph->state == QUE_FORK_COMMAND_WAIT) { |
684 | |
685 | thr->state = QUE_THR_SUSPENDED; |
686 | |
687 | } else if (trx->lock.que_state == TRX_QUE_LOCK_WAIT) { |
688 | |
689 | trx->lock.wait_thr = thr; |
690 | thr->state = QUE_THR_LOCK_WAIT; |
691 | |
692 | } else if (trx->duplicates && trx->error_state == DB_DUPLICATE_KEY) { |
693 | |
694 | return(FALSE); |
695 | |
696 | } else if (trx->error_state != DB_SUCCESS |
697 | && trx->error_state != DB_LOCK_WAIT) { |
698 | |
699 | /* Error handling built for the MySQL interface */ |
700 | thr->state = QUE_THR_COMPLETED; |
701 | |
702 | } else if (graph->fork_type == QUE_FORK_ROLLBACK) { |
703 | |
704 | thr->state = QUE_THR_SUSPENDED; |
705 | } else { |
706 | ut_ad(graph->state == QUE_FORK_ACTIVE); |
707 | |
708 | return(FALSE); |
709 | } |
710 | |
711 | return(TRUE); |
712 | } |
713 | |
714 | /**********************************************************************//** |
715 | Decrements the query thread reference counts in the query graph and the |
716 | transaction. |
717 | *** NOTE ***: |
718 | This and que_thr_stop_for_mysql are the only functions where the reference |
719 | count can be decremented and this function may only be called from inside |
720 | que_run_threads! These restrictions exist to make the rollback code easier |
721 | to maintain. */ |
722 | static |
723 | void |
724 | que_thr_dec_refer_count( |
725 | /*====================*/ |
726 | que_thr_t* thr, /*!< in: query thread */ |
727 | que_thr_t** next_thr) /*!< in/out: next query thread to run; |
728 | if the value which is passed in is |
729 | a pointer to a NULL pointer, then the |
730 | calling function can start running |
731 | a new query thread */ |
732 | { |
733 | trx_t* trx; |
734 | que_fork_t* fork; |
735 | |
736 | trx = thr_get_trx(thr); |
737 | |
738 | ut_a(thr->is_active); |
739 | ut_ad(trx_mutex_own(trx)); |
740 | |
741 | if (thr->state == QUE_THR_RUNNING) { |
742 | |
743 | if (!que_thr_stop(thr)) { |
744 | |
745 | ut_a(next_thr != NULL && *next_thr == NULL); |
746 | |
747 | /* The reason for the thr suspension or wait was |
748 | already canceled before we came here: continue |
749 | running the thread. |
750 | |
751 | This is also possible because in trx_commit_step() we |
752 | assume a single query thread. We set the query thread |
753 | state to QUE_THR_RUNNING. */ |
754 | |
755 | /* fprintf(stderr, |
756 | "Wait already ended: trx: %p\n", trx); */ |
757 | |
758 | /* Normally srv_suspend_mysql_thread resets |
759 | the state to DB_SUCCESS before waiting, but |
760 | in this case we have to do it here, |
761 | otherwise nobody does it. */ |
762 | |
763 | trx->error_state = DB_SUCCESS; |
764 | |
765 | *next_thr = thr; |
766 | |
767 | return; |
768 | } |
769 | } |
770 | |
771 | fork = static_cast<que_fork_t*>(thr->common.parent); |
772 | |
773 | --trx->lock.n_active_thrs; |
774 | |
775 | --fork->n_active_thrs; |
776 | |
777 | thr->is_active = FALSE; |
778 | } |
779 | |
780 | /**********************************************************************//** |
781 | A patch for MySQL used to 'stop' a dummy query thread used in MySQL. The |
782 | query thread is stopped and made inactive, except in the case where |
783 | it was put to the lock wait state in lock0lock.cc, but the lock has already |
784 | been granted or the transaction chosen as a victim in deadlock resolution. */ |
785 | void |
786 | que_thr_stop_for_mysql( |
787 | /*===================*/ |
788 | que_thr_t* thr) /*!< in: query thread */ |
789 | { |
790 | trx_t* trx; |
791 | |
792 | trx = thr_get_trx(thr); |
793 | |
794 | trx_mutex_enter(trx); |
795 | |
796 | if (thr->state == QUE_THR_RUNNING) { |
797 | |
798 | if (trx->error_state != DB_SUCCESS |
799 | && trx->error_state != DB_LOCK_WAIT) { |
800 | |
801 | /* Error handling built for the MySQL interface */ |
802 | thr->state = QUE_THR_COMPLETED; |
803 | } else { |
804 | /* It must have been a lock wait but the lock was |
805 | already released, or this transaction was chosen |
806 | as a victim in selective deadlock resolution */ |
807 | |
808 | trx_mutex_exit(trx); |
809 | |
810 | return; |
811 | } |
812 | } |
813 | |
814 | ut_ad(thr->is_active == TRUE); |
815 | ut_ad(trx->lock.n_active_thrs == 1); |
816 | ut_ad(thr->graph->n_active_thrs == 1); |
817 | |
818 | thr->is_active = FALSE; |
819 | thr->graph->n_active_thrs--; |
820 | |
821 | trx->lock.n_active_thrs--; |
822 | |
823 | trx_mutex_exit(trx); |
824 | } |
825 | |
826 | /**********************************************************************//** |
827 | Moves a thread from another state to the QUE_THR_RUNNING state. Increments |
828 | the n_active_thrs counters of the query graph and transaction if thr was |
829 | not active. */ |
830 | void |
831 | que_thr_move_to_run_state_for_mysql( |
832 | /*================================*/ |
833 | que_thr_t* thr, /*!< in: an query thread */ |
834 | trx_t* trx) /*!< in: transaction */ |
835 | { |
836 | ut_a(thr->magic_n == QUE_THR_MAGIC_N); |
837 | |
838 | if (!thr->is_active) { |
839 | |
840 | thr->graph->n_active_thrs++; |
841 | |
842 | trx->lock.n_active_thrs++; |
843 | |
844 | thr->is_active = TRUE; |
845 | } |
846 | |
847 | thr->state = QUE_THR_RUNNING; |
848 | } |
849 | |
850 | /**********************************************************************//** |
851 | A patch for MySQL used to 'stop' a dummy query thread used in MySQL |
852 | select, when there is no error or lock wait. */ |
853 | void |
854 | que_thr_stop_for_mysql_no_error( |
855 | /*============================*/ |
856 | que_thr_t* thr, /*!< in: query thread */ |
857 | trx_t* trx) /*!< in: transaction */ |
858 | { |
859 | ut_ad(thr->state == QUE_THR_RUNNING); |
860 | ut_ad(thr->is_active == TRUE); |
861 | ut_ad(trx->lock.n_active_thrs == 1); |
862 | ut_ad(thr->graph->n_active_thrs == 1); |
863 | ut_a(thr->magic_n == QUE_THR_MAGIC_N); |
864 | |
865 | thr->state = QUE_THR_COMPLETED; |
866 | |
867 | thr->is_active = FALSE; |
868 | thr->graph->n_active_thrs--; |
869 | |
870 | trx->lock.n_active_thrs--; |
871 | } |
872 | |
873 | /****************************************************************//** |
874 | Get the first containing loop node (e.g. while_node_t or for_node_t) for the |
875 | given node, or NULL if the node is not within a loop. |
876 | @return containing loop node, or NULL. */ |
877 | que_node_t* |
878 | que_node_get_containing_loop_node( |
879 | /*==============================*/ |
880 | que_node_t* node) /*!< in: node */ |
881 | { |
882 | ut_ad(node); |
883 | |
884 | for (;;) { |
885 | ulint type; |
886 | |
887 | node = que_node_get_parent(node); |
888 | |
889 | if (!node) { |
890 | break; |
891 | } |
892 | |
893 | type = que_node_get_type(node); |
894 | |
895 | if ((type == QUE_NODE_FOR) || (type == QUE_NODE_WHILE)) { |
896 | break; |
897 | } |
898 | } |
899 | |
900 | return(node); |
901 | } |
902 | |
903 | #ifndef DBUG_OFF |
904 | /** Gets information of an SQL query graph node. |
905 | @return type description */ |
906 | static MY_ATTRIBUTE((warn_unused_result, nonnull)) |
907 | const char* |
908 | que_node_type_string( |
909 | /*=================*/ |
910 | const que_node_t* node) /*!< in: query graph node */ |
911 | { |
912 | switch (que_node_get_type(node)) { |
913 | case QUE_NODE_SELECT: |
914 | return("SELECT" ); |
915 | case QUE_NODE_INSERT: |
916 | return("INSERT" ); |
917 | case QUE_NODE_UPDATE: |
918 | return("UPDATE" ); |
919 | case QUE_NODE_WHILE: |
920 | return("WHILE" ); |
921 | case QUE_NODE_ASSIGNMENT: |
922 | return("ASSIGNMENT" ); |
923 | case QUE_NODE_IF: |
924 | return("IF" ); |
925 | case QUE_NODE_FETCH: |
926 | return("FETCH" ); |
927 | case QUE_NODE_OPEN: |
928 | return("OPEN" ); |
929 | case QUE_NODE_PROC: |
930 | return("STORED PROCEDURE" ); |
931 | case QUE_NODE_FUNC: |
932 | return("FUNCTION" ); |
933 | case QUE_NODE_LOCK: |
934 | return("LOCK" ); |
935 | case QUE_NODE_THR: |
936 | return("QUERY THREAD" ); |
937 | case QUE_NODE_COMMIT: |
938 | return("COMMIT" ); |
939 | case QUE_NODE_UNDO: |
940 | return("UNDO ROW" ); |
941 | case QUE_NODE_PURGE: |
942 | return("PURGE ROW" ); |
943 | case QUE_NODE_ROLLBACK: |
944 | return("ROLLBACK" ); |
945 | case QUE_NODE_CREATE_TABLE: |
946 | return("CREATE TABLE" ); |
947 | case QUE_NODE_CREATE_INDEX: |
948 | return("CREATE INDEX" ); |
949 | case QUE_NODE_FOR: |
950 | return("FOR LOOP" ); |
951 | case QUE_NODE_RETURN: |
952 | return("RETURN" ); |
953 | case QUE_NODE_EXIT: |
954 | return("EXIT" ); |
955 | default: |
956 | ut_ad(0); |
957 | return("UNKNOWN NODE TYPE" ); |
958 | } |
959 | } |
960 | #endif /* !DBUG_OFF */ |
961 | |
962 | /**********************************************************************//** |
963 | Performs an execution step on a query thread. |
964 | @return query thread to run next: it may differ from the input |
965 | parameter if, e.g., a subprocedure call is made */ |
966 | UNIV_INLINE |
967 | que_thr_t* |
968 | que_thr_step( |
969 | /*=========*/ |
970 | que_thr_t* thr) /*!< in: query thread */ |
971 | { |
972 | que_node_t* node; |
973 | que_thr_t* old_thr; |
974 | trx_t* trx; |
975 | ulint type; |
976 | |
977 | trx = thr_get_trx(thr); |
978 | |
979 | ut_ad(thr->state == QUE_THR_RUNNING); |
980 | ut_a(trx->error_state == DB_SUCCESS); |
981 | |
982 | thr->resource++; |
983 | |
984 | node = thr->run_node; |
985 | type = que_node_get_type(node); |
986 | |
987 | old_thr = thr; |
988 | |
989 | DBUG_PRINT("ib_que" , ("Execute %u (%s) at %p" , |
990 | unsigned(type), que_node_type_string(node), |
991 | (const void*) node)); |
992 | |
993 | if (type & QUE_NODE_CONTROL_STAT) { |
994 | if ((thr->prev_node != que_node_get_parent(node)) |
995 | && que_node_get_next(thr->prev_node)) { |
996 | |
997 | /* The control statements, like WHILE, always pass the |
998 | control to the next child statement if there is any |
999 | child left */ |
1000 | |
1001 | thr->run_node = que_node_get_next(thr->prev_node); |
1002 | |
1003 | } else if (type == QUE_NODE_IF) { |
1004 | if_step(thr); |
1005 | } else if (type == QUE_NODE_FOR) { |
1006 | for_step(thr); |
1007 | } else if (type == QUE_NODE_PROC) { |
1008 | if (thr->prev_node == que_node_get_parent(node)) { |
1009 | trx->last_sql_stat_start.least_undo_no |
1010 | = trx->undo_no; |
1011 | } |
1012 | |
1013 | proc_step(thr); |
1014 | } else if (type == QUE_NODE_WHILE) { |
1015 | while_step(thr); |
1016 | } else { |
1017 | ut_error; |
1018 | } |
1019 | } else if (type == QUE_NODE_ASSIGNMENT) { |
1020 | assign_step(thr); |
1021 | } else if (type == QUE_NODE_SELECT) { |
1022 | thr = row_sel_step(thr); |
1023 | } else if (type == QUE_NODE_INSERT) { |
1024 | trx_start_if_not_started_xa(thr_get_trx(thr), true); |
1025 | thr = row_ins_step(thr); |
1026 | } else if (type == QUE_NODE_UPDATE) { |
1027 | trx_start_if_not_started_xa(thr_get_trx(thr), true); |
1028 | thr = row_upd_step(thr); |
1029 | } else if (type == QUE_NODE_FETCH) { |
1030 | thr = fetch_step(thr); |
1031 | } else if (type == QUE_NODE_OPEN) { |
1032 | thr = open_step(thr); |
1033 | } else if (type == QUE_NODE_FUNC) { |
1034 | proc_eval_step(thr); |
1035 | |
1036 | } else if (type == QUE_NODE_LOCK) { |
1037 | |
1038 | ut_error; |
1039 | } else if (type == QUE_NODE_THR) { |
1040 | thr = que_thr_node_step(thr); |
1041 | } else if (type == QUE_NODE_COMMIT) { |
1042 | thr = trx_commit_step(thr); |
1043 | } else if (type == QUE_NODE_UNDO) { |
1044 | thr = row_undo_step(thr); |
1045 | } else if (type == QUE_NODE_PURGE) { |
1046 | thr = row_purge_step(thr); |
1047 | } else if (type == QUE_NODE_RETURN) { |
1048 | thr = return_step(thr); |
1049 | } else if (type == QUE_NODE_EXIT) { |
1050 | thr = exit_step(thr); |
1051 | } else if (type == QUE_NODE_ROLLBACK) { |
1052 | thr = trx_rollback_step(thr); |
1053 | } else if (type == QUE_NODE_CREATE_TABLE) { |
1054 | thr = dict_create_table_step(thr); |
1055 | } else if (type == QUE_NODE_CREATE_INDEX) { |
1056 | thr = dict_create_index_step(thr); |
1057 | } else if (type == QUE_NODE_ROW_PRINTF) { |
1058 | thr = row_printf_step(thr); |
1059 | } else { |
1060 | ut_error; |
1061 | } |
1062 | |
1063 | if (type == QUE_NODE_EXIT) { |
1064 | old_thr->prev_node = que_node_get_containing_loop_node(node); |
1065 | } else { |
1066 | old_thr->prev_node = node; |
1067 | } |
1068 | |
1069 | if (thr) { |
1070 | ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); |
1071 | } |
1072 | |
1073 | return(thr); |
1074 | } |
1075 | |
1076 | /**********************************************************************//** |
1077 | Run a query thread until it finishes or encounters e.g. a lock wait. */ |
1078 | static |
1079 | void |
1080 | que_run_threads_low( |
1081 | /*================*/ |
1082 | que_thr_t* thr) /*!< in: query thread */ |
1083 | { |
1084 | trx_t* trx; |
1085 | que_thr_t* next_thr; |
1086 | |
1087 | ut_ad(thr->state == QUE_THR_RUNNING); |
1088 | ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); |
1089 | ut_ad(!trx_mutex_own(thr_get_trx(thr))); |
1090 | |
1091 | /* cumul_resource counts how much resources the OS thread (NOT the |
1092 | query thread) has spent in this function */ |
1093 | |
1094 | trx = thr_get_trx(thr); |
1095 | |
1096 | do { |
1097 | /* Check that there is enough space in the log to accommodate |
1098 | possible log entries by this query step; if the operation can |
1099 | touch more than about 4 pages, checks must be made also within |
1100 | the query step! */ |
1101 | |
1102 | log_free_check(); |
1103 | |
1104 | /* Perform the actual query step: note that the query thread |
1105 | may change if, e.g., a subprocedure call is made */ |
1106 | |
1107 | /*-------------------------*/ |
1108 | next_thr = que_thr_step(thr); |
1109 | /*-------------------------*/ |
1110 | |
1111 | trx_mutex_enter(trx); |
1112 | |
1113 | ut_a(next_thr == NULL || trx->error_state == DB_SUCCESS); |
1114 | |
1115 | if (next_thr != thr) { |
1116 | ut_a(next_thr == NULL); |
1117 | |
1118 | /* This can change next_thr to a non-NULL value |
1119 | if there was a lock wait that already completed. */ |
1120 | |
1121 | que_thr_dec_refer_count(thr, &next_thr); |
1122 | |
1123 | if (next_thr != NULL) { |
1124 | |
1125 | thr = next_thr; |
1126 | } |
1127 | } |
1128 | |
1129 | ut_ad(trx == thr_get_trx(thr)); |
1130 | |
1131 | trx_mutex_exit(trx); |
1132 | |
1133 | } while (next_thr != NULL); |
1134 | } |
1135 | |
1136 | /**********************************************************************//** |
1137 | Run a query thread. Handles lock waits. */ |
1138 | void |
1139 | que_run_threads( |
1140 | /*============*/ |
1141 | que_thr_t* thr) /*!< in: query thread */ |
1142 | { |
1143 | ut_ad(!trx_mutex_own(thr_get_trx(thr))); |
1144 | |
1145 | loop: |
1146 | ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); |
1147 | |
1148 | que_run_threads_low(thr); |
1149 | |
1150 | switch (thr->state) { |
1151 | |
1152 | case QUE_THR_RUNNING: |
1153 | /* There probably was a lock wait, but it already ended |
1154 | before we came here: continue running thr */ |
1155 | |
1156 | goto loop; |
1157 | |
1158 | case QUE_THR_LOCK_WAIT: |
1159 | lock_wait_suspend_thread(thr); |
1160 | |
1161 | trx_mutex_enter(thr_get_trx(thr)); |
1162 | |
1163 | ut_a(thr_get_trx(thr)->id != 0); |
1164 | |
1165 | if (thr_get_trx(thr)->error_state != DB_SUCCESS) { |
1166 | /* thr was chosen as a deadlock victim or there was |
1167 | a lock wait timeout */ |
1168 | |
1169 | que_thr_dec_refer_count(thr, NULL); |
1170 | trx_mutex_exit(thr_get_trx(thr)); |
1171 | break; |
1172 | } |
1173 | |
1174 | trx_mutex_exit(thr_get_trx(thr)); |
1175 | goto loop; |
1176 | |
1177 | case QUE_THR_COMPLETED: |
1178 | case QUE_THR_COMMAND_WAIT: |
1179 | /* Do nothing */ |
1180 | break; |
1181 | |
1182 | default: |
1183 | ut_error; |
1184 | } |
1185 | } |
1186 | |
1187 | /*********************************************************************//** |
1188 | Evaluate the given SQL. |
1189 | @return error code or DB_SUCCESS */ |
1190 | dberr_t |
1191 | que_eval_sql( |
1192 | /*=========*/ |
1193 | pars_info_t* info, /*!< in: info struct, or NULL */ |
1194 | const char* sql, /*!< in: SQL string */ |
1195 | ibool reserve_dict_mutex, |
1196 | /*!< in: if TRUE, acquire/release |
1197 | dict_sys->mutex around call to pars_sql. */ |
1198 | trx_t* trx) /*!< in: trx */ |
1199 | { |
1200 | que_thr_t* thr; |
1201 | que_t* graph; |
1202 | |
1203 | DBUG_ENTER("que_eval_sql" ); |
1204 | DBUG_PRINT("que_eval_sql" , ("query: %s" , sql)); |
1205 | |
1206 | ut_a(trx->error_state == DB_SUCCESS); |
1207 | |
1208 | if (reserve_dict_mutex) { |
1209 | mutex_enter(&dict_sys->mutex); |
1210 | } |
1211 | |
1212 | graph = pars_sql(info, sql); |
1213 | |
1214 | if (reserve_dict_mutex) { |
1215 | mutex_exit(&dict_sys->mutex); |
1216 | } |
1217 | |
1218 | graph->trx = trx; |
1219 | trx->graph = NULL; |
1220 | |
1221 | graph->fork_type = QUE_FORK_MYSQL_INTERFACE; |
1222 | |
1223 | ut_a(thr = que_fork_start_command(graph)); |
1224 | |
1225 | que_run_threads(thr); |
1226 | |
1227 | if (reserve_dict_mutex) { |
1228 | mutex_enter(&dict_sys->mutex); |
1229 | } |
1230 | |
1231 | que_graph_free(graph); |
1232 | |
1233 | if (reserve_dict_mutex) { |
1234 | mutex_exit(&dict_sys->mutex); |
1235 | } |
1236 | |
1237 | ut_a(trx->error_state != 0); |
1238 | |
1239 | DBUG_RETURN(trx->error_state); |
1240 | } |
1241 | |