00001 /* Copyright (C) 2005 MySQL AB & MySQL Finland AB & TCX DataKonsult AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 #ifdef USE_PRAGMA_IMPLEMENTATION 00017 #pragma implementation /* gcc class implementation */ 00018 #endif 00019 00020 #include "mysql_priv.h" 00021 #include "sql_cursor.h" 00022 #include "sql_select.h" 00023 00024 /**************************************************************************** 00025 Declarations. 00026 ****************************************************************************/ 00027 00028 /* 00029 Sensitive_cursor -- a sensitive non-materialized server side 00030 cursor An instance of this class cursor has its own runtime 00031 state -- list of used items and memory root for runtime memory, 00032 open and locked tables, change list for the changes of the 00033 parsed tree. This state is freed when the cursor is closed. 00034 */ 00035 00036 class Sensitive_cursor: public Server_side_cursor 00037 { 00038 MEM_ROOT main_mem_root; 00039 Query_arena *stmt_arena; 00040 JOIN *join; 00041 TABLE *open_tables; 00042 MYSQL_LOCK *lock; 00043 TABLE *derived_tables; 00044 /* List of items created during execution */ 00045 query_id_t query_id; 00046 struct Engine_info 00047 { 00048 const handlerton *ht; 00049 void *read_view; 00050 }; 00051 Engine_info ht_info[MAX_HA]; 00052 Item_change_list change_list; 00053 my_bool close_at_commit; 00054 THR_LOCK_OWNER lock_id; 00055 private: 00056 /* bzero cursor state in THD */ 00057 void reset_thd(THD *thd); 00058 public: 00059 Sensitive_cursor(THD *thd, select_result *result_arg); 00060 00061 THR_LOCK_OWNER *get_lock_id() { return &lock_id; } 00062 /* Save THD state into cursor */ 00063 void post_open(THD *thd); 00064 00065 virtual bool is_open() const { return join != 0; } 00066 virtual int open(JOIN *join); 00067 virtual void fetch(ulong num_rows); 00068 virtual void close(); 00069 virtual ~Sensitive_cursor(); 00070 }; 00071 00072 00073 /* 00074 Materialized_cursor -- an insensitive materialized server-side 00075 cursor. The result set of this cursor is saved in a temporary 00076 table at open. The cursor itself is simply an interface for the 00077 handler of the temporary table. 00078 */ 00079 00080 class Materialized_cursor: public Server_side_cursor 00081 { 00082 MEM_ROOT main_mem_root; 00083 /* A fake unit to supply to select_send when fetching */ 00084 SELECT_LEX_UNIT fake_unit; 00085 TABLE *table; 00086 List<Item> item_list; 00087 ulong fetch_limit; 00088 ulong fetch_count; 00089 public: 00090 Materialized_cursor(select_result *result, TABLE *table); 00091 00092 virtual bool is_open() const { return table != 0; } 00093 virtual int open(JOIN *join __attribute__((unused))); 00094 virtual void fetch(ulong num_rows); 00095 virtual void close(); 00096 virtual ~Materialized_cursor(); 00097 }; 00098 00099 00100 /* 00101 Select_materialize -- a mediator between a cursor query and the 00102 protocol. In case we were not able to open a non-materialzed 00103 cursor, it creates an internal temporary HEAP table, and insert 00104 all rows into it. When the table reaches max_heap_table_size, 00105 it's converted to a MyISAM table. Later this table is used to 00106 create a Materialized_cursor. 00107 */ 00108 00109 class Select_materialize: public select_union 00110 { 00111 select_result *result; /* the result object of the caller (PS or SP) */ 00112 public: 00113 Select_materialize(select_result *result_arg) :result(result_arg) {} 00114 virtual bool send_fields(List<Item> &list, uint flags); 00115 }; 00116 00117 00118 /**************************************************************************/ 00119 00120 /* 00121 Attempt to open a materialized or non-materialized cursor. 00122 00123 SYNOPSIS 00124 mysql_open_cursor() 00125 thd thread handle 00126 flags [in] create a materialized cursor or not 00127 result [in] result class of the caller used as a destination 00128 for the rows fetched from the cursor 00129 pcursor [out] a pointer to store a pointer to cursor in 00130 00131 RETURN VALUE 00132 0 the query has been successfully executed; in this 00133 case pcursor may or may not contain 00134 a pointer to an open cursor. 00135 non-zero an error, 'pcursor' has been left intact. 00136 */ 00137 00138 int mysql_open_cursor(THD *thd, uint flags, select_result *result, 00139 Server_side_cursor **pcursor) 00140 { 00141 Sensitive_cursor *sensitive_cursor; 00142 select_result *save_result; 00143 Select_materialize *result_materialize; 00144 LEX *lex= thd->lex; 00145 int rc; 00146 00147 /* 00148 The lifetime of the sensitive cursor is the same or less as the 00149 lifetime of the runtime memory of the statement it's opened for. 00150 */ 00151 if (! (result_materialize= new (thd->mem_root) Select_materialize(result))) 00152 return 1; 00153 00154 if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result))) 00155 { 00156 delete result; 00157 return 1; 00158 } 00159 00160 save_result= lex->result; 00161 00162 lex->result= result_materialize; 00163 if (! (flags & (uint) ALWAYS_MATERIALIZED_CURSOR)) 00164 { 00165 thd->lock_id= sensitive_cursor->get_lock_id(); 00166 thd->cursor= sensitive_cursor; 00167 } 00168 00169 rc= mysql_execute_command(thd); 00170 00171 lex->result= save_result; 00172 thd->lock_id= &thd->main_lock_id; 00173 thd->cursor= 0; 00174 00175 /* 00176 Possible options here: 00177 - a sensitive cursor is open. In this case rc is 0 and 00178 result_materialize->table is NULL, or 00179 - a materialized cursor is open. In this case rc is 0 and 00180 result_materialize->table is not NULL 00181 - an error occured during materializaton. 00182 result_materialize->table is not NULL, but rc != 0 00183 - successful completion of mysql_execute_command without 00184 a cursor: rc is 0, result_materialize->table is NULL, 00185 sensitive_cursor is not open. 00186 This is possible if some command writes directly to the 00187 network, bypassing select_result mechanism. An example of 00188 such command is SHOW VARIABLES or SHOW STATUS. 00189 */ 00190 if (rc) 00191 goto err_open; 00192 00193 if (sensitive_cursor->is_open()) 00194 { 00195 DBUG_ASSERT(!result_materialize->table); 00196 /* 00197 It's safer if we grab THD state after mysql_execute_command 00198 is finished and not in Sensitive_cursor::open(), because 00199 currently the call to Sensitive_cursor::open is buried deep 00200 in JOIN::exec of the top level join. 00201 */ 00202 sensitive_cursor->post_open(thd); 00203 *pcursor= sensitive_cursor; 00204 goto end; 00205 } 00206 else if (result_materialize->table) 00207 { 00208 Materialized_cursor *materialized_cursor; 00209 TABLE *table= result_materialize->table; 00210 MEM_ROOT *mem_root= &table->mem_root; 00211 00212 if (!(materialized_cursor= new (mem_root) 00213 Materialized_cursor(result, table))) 00214 { 00215 rc= 1; 00216 goto err_open; 00217 } 00218 00219 if ((rc= materialized_cursor->open(0))) 00220 { 00221 delete materialized_cursor; 00222 goto err_open; 00223 } 00224 00225 *pcursor= materialized_cursor; 00226 thd->stmt_arena->cleanup_stmt(); 00227 goto end; 00228 } 00229 00230 err_open: 00231 DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open())); 00232 delete sensitive_cursor; 00233 if (result_materialize->table) 00234 free_tmp_table(thd, result_materialize->table); 00235 end: 00236 delete result_materialize; 00237 return rc; 00238 } 00239 00240 /**************************************************************************** 00241 Server_side_cursor 00242 ****************************************************************************/ 00243 00244 Server_side_cursor::~Server_side_cursor() 00245 { 00246 } 00247 00248 00249 void Server_side_cursor::operator delete(void *ptr, size_t size) 00250 { 00251 Server_side_cursor *cursor= (Server_side_cursor*) ptr; 00252 MEM_ROOT own_root= *cursor->mem_root; 00253 00254 DBUG_ENTER("Server_side_cursor::operator delete"); 00255 TRASH(ptr, size); 00256 /* 00257 If this cursor has never been opened mem_root is empty. Otherwise 00258 mem_root points to the memory the cursor object was allocated in. 00259 In this case it's important to call free_root last, and free a copy 00260 instead of *mem_root to avoid writing into freed memory. 00261 */ 00262 free_root(&own_root, MYF(0)); 00263 DBUG_VOID_RETURN; 00264 } 00265 00266 /**************************************************************************** 00267 Sensitive_cursor 00268 ****************************************************************************/ 00269 00270 Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg) 00271 :Server_side_cursor(&main_mem_root, result_arg), 00272 stmt_arena(0), 00273 join(0), 00274 close_at_commit(FALSE) 00275 { 00276 /* We will overwrite it at open anyway. */ 00277 init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0); 00278 thr_lock_owner_init(&lock_id, &thd->lock_info); 00279 bzero((void*) ht_info, sizeof(ht_info)); 00280 } 00281 00282 00283 void 00284 Sensitive_cursor::post_open(THD *thd) 00285 { 00286 Engine_info *info; 00287 /* 00288 We need to save and reset thd->mem_root, otherwise it'll be 00289 freed later in mysql_parse. 00290 00291 We can't just change thd->mem_root here as we want to keep the 00292 things that are already allocated in thd->mem_root for 00293 Sensitive_cursor::fetch() 00294 */ 00295 *mem_root= *thd->mem_root; 00296 stmt_arena= thd->stmt_arena; 00297 state= stmt_arena->state; 00298 /* Allocate a new memory root for thd */ 00299 init_sql_alloc(thd->mem_root, 00300 thd->variables.query_alloc_block_size, 00301 thd->variables.query_prealloc_size); 00302 00303 /* 00304 Save tables and zero THD pointers to prevent table close in 00305 close_thread_tables. 00306 */ 00307 derived_tables= thd->derived_tables; 00308 open_tables= thd->open_tables; 00309 lock= thd->lock; 00310 query_id= thd->query_id; 00311 free_list= thd->free_list; 00312 change_list= thd->change_list; 00313 reset_thd(thd); 00314 /* Now we have an active cursor and can cause a deadlock */ 00315 thd->lock_info.n_cursors++; 00316 00317 close_at_commit= FALSE; /* reset in case we're reusing the cursor */ 00318 info= &ht_info[0]; 00319 for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++) 00320 { 00321 const handlerton *ht= *pht; 00322 close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT); 00323 if (ht->create_cursor_read_view) 00324 { 00325 info->ht= ht; 00326 info->read_view= (ht->create_cursor_read_view)(); 00327 ++info; 00328 } 00329 } 00330 /* 00331 XXX: thd->locked_tables is not changed. 00332 What problems can we have with it if cursor is open? 00333 TODO: must be fixed because of the prelocked mode. 00334 */ 00335 } 00336 00337 00338 void 00339 Sensitive_cursor::reset_thd(THD *thd) 00340 { 00341 thd->derived_tables= 0; 00342 thd->open_tables= 0; 00343 thd->lock= 0; 00344 thd->free_list= 0; 00345 thd->change_list.empty(); 00346 } 00347 00348 00349 int 00350 Sensitive_cursor::open(JOIN *join_arg) 00351 { 00352 join= join_arg; 00353 THD *thd= join->thd; 00354 /* First non-constant table */ 00355 JOIN_TAB *join_tab= join->join_tab + join->const_tables; 00356 DBUG_ENTER("Sensitive_cursor::open"); 00357 00358 join->change_result(result); 00359 /* 00360 Send fields description to the client; server_status is sent 00361 in 'EOF' packet, which follows send_fields(). 00362 We don't simply use SEND_EOF flag of send_fields because we also 00363 want to flush the network buffer, which is done only in a standalone 00364 send_eof(). 00365 */ 00366 result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); 00367 thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; 00368 result->send_eof(); 00369 thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; 00370 00371 /* Prepare JOIN for reading rows. */ 00372 join->tmp_table= 0; 00373 join->join_tab[join->tables-1].next_select= setup_end_select_func(join); 00374 join->send_records= 0; 00375 join->fetch_limit= join->unit->offset_limit_cnt; 00376 00377 /* Disable JOIN CACHE as it is not working with cursors yet */ 00378 for (JOIN_TAB *tab= join_tab; 00379 tab != join->join_tab + join->tables - 1; 00380 tab++) 00381 { 00382 if (tab->next_select == sub_select_cache) 00383 tab->next_select= sub_select; 00384 } 00385 00386 DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); 00387 DBUG_ASSERT(join_tab->not_used_in_distinct == 0); 00388 /* 00389 null_row is set only if row not found and it's outer join: should never 00390 happen for the first table in join_tab list 00391 */ 00392 DBUG_ASSERT(join_tab->table->null_row == 0); 00393 DBUG_RETURN(0); 00394 } 00395 00396 00397 /* 00398 SYNOPSIS 00399 Sensitive_cursor::fetch() 00400 num_rows fetch up to this number of rows (maybe less) 00401 00402 DESCRIPTION 00403 Fetch next num_rows rows from the cursor and send them to the client 00404 00405 Precondition: 00406 Sensitive_cursor is open 00407 00408 RETURN VALUES: 00409 none, this function will send OK to the clinet or set an error 00410 message in THD 00411 */ 00412 00413 void 00414 Sensitive_cursor::fetch(ulong num_rows) 00415 { 00416 THD *thd= join->thd; 00417 JOIN_TAB *join_tab= join->join_tab + join->const_tables; 00418 enum_nested_loop_state error= NESTED_LOOP_OK; 00419 Query_arena backup_arena; 00420 Engine_info *info; 00421 DBUG_ENTER("Sensitive_cursor::fetch"); 00422 DBUG_PRINT("enter",("rows: %lu", num_rows)); 00423 00424 DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 && 00425 thd->lock == 0); 00426 00427 thd->derived_tables= derived_tables; 00428 thd->open_tables= open_tables; 00429 thd->lock= lock; 00430 thd->query_id= query_id; 00431 thd->change_list= change_list; 00432 /* save references to memory allocated during fetch */ 00433 thd->set_n_backup_active_arena(this, &backup_arena); 00434 00435 for (info= ht_info; info->read_view ; info++) 00436 (info->ht->set_cursor_read_view)(info->read_view); 00437 00438 join->fetch_limit+= num_rows; 00439 00440 error= sub_select(join, join_tab, 0); 00441 if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS) 00442 error= sub_select(join,join_tab,1); 00443 if (error == NESTED_LOOP_QUERY_LIMIT) 00444 error= NESTED_LOOP_OK; /* select_limit used */ 00445 if (error == NESTED_LOOP_CURSOR_LIMIT) 00446 join->resume_nested_loop= TRUE; 00447 00448 ha_release_temporary_latches(thd); 00449 00450 /* Grab free_list here to correctly free it in close */ 00451 thd->restore_active_arena(this, &backup_arena); 00452 00453 change_list= thd->change_list; 00454 reset_thd(thd); 00455 00456 for (info= ht_info; info->read_view; info++) 00457 (info->ht->set_cursor_read_view)(0); 00458 00459 if (error == NESTED_LOOP_CURSOR_LIMIT) 00460 { 00461 /* Fetch limit worked, possibly more rows are there */ 00462 thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; 00463 result->send_eof(); 00464 thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; 00465 } 00466 else 00467 { 00468 close(); 00469 if (error == NESTED_LOOP_OK) 00470 { 00471 thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; 00472 result->send_eof(); 00473 thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; 00474 } 00475 else if (error != NESTED_LOOP_KILLED) 00476 my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); 00477 } 00478 DBUG_VOID_RETURN; 00479 } 00480 00481 00482 void 00483 Sensitive_cursor::close() 00484 { 00485 THD *thd= join->thd; 00486 DBUG_ENTER("Sensitive_cursor::close"); 00487 00488 for (Engine_info *info= ht_info; info->read_view; info++) 00489 { 00490 (info->ht->close_cursor_read_view)(info->read_view); 00491 info->read_view= 0; 00492 info->ht= 0; 00493 } 00494 00495 thd->change_list= change_list; 00496 { 00497 /* 00498 XXX: Another hack: we need to set THD state as if in a fetch to be 00499 able to call stmt close. 00500 */ 00501 DBUG_ASSERT(lock || open_tables || derived_tables); 00502 00503 TABLE *tmp_derived_tables= thd->derived_tables; 00504 MYSQL_LOCK *tmp_lock= thd->lock; 00505 00506 thd->open_tables= open_tables; 00507 thd->derived_tables= derived_tables; 00508 thd->lock= lock; 00509 00510 /* Is expected to at least close tables and empty thd->change_list */ 00511 stmt_arena->cleanup_stmt(); 00512 00513 thd->open_tables= tmp_derived_tables; 00514 thd->derived_tables= tmp_derived_tables; 00515 thd->lock= tmp_lock; 00516 } 00517 thd->lock_info.n_cursors--; /* Decrease the number of active cursors */ 00518 join= 0; 00519 stmt_arena= 0; 00520 free_items(); 00521 change_list.empty(); 00522 DBUG_VOID_RETURN; 00523 } 00524 00525 00526 Sensitive_cursor::~Sensitive_cursor() 00527 { 00528 if (is_open()) 00529 close(); 00530 } 00531 00532 /*************************************************************************** 00533 Materialized_cursor 00534 ****************************************************************************/ 00535 00536 Materialized_cursor::Materialized_cursor(select_result *result_arg, 00537 TABLE *table_arg) 00538 :Server_side_cursor(&table_arg->mem_root, result_arg), 00539 table(table_arg), 00540 fetch_limit(0), 00541 fetch_count(0) 00542 { 00543 fake_unit.init_query(); 00544 fake_unit.thd= table->in_use; 00545 } 00546 00547 00548 int Materialized_cursor::open(JOIN *join __attribute__((unused))) 00549 { 00550 THD *thd= fake_unit.thd; 00551 int rc; 00552 Query_arena backup_arena; 00553 00554 thd->set_n_backup_active_arena(this, &backup_arena); 00555 /* Create a list of fields and start sequential scan */ 00556 rc= (table->fill_item_list(&item_list) || 00557 result->prepare(item_list, &fake_unit) || 00558 table->file->ha_rnd_init(TRUE)); 00559 thd->restore_active_arena(this, &backup_arena); 00560 if (rc == 0) 00561 { 00562 /* 00563 Now send the result set metadata to the client. We need to 00564 do it here, as in Select_materialize::send_fields the items 00565 for column types are not yet created (send_fields requires 00566 a list of items). The new types may differ from the original 00567 ones sent at prepare if some of them were altered by MySQL 00568 HEAP tables mechanism -- used when create_tmp_field_from_item 00569 may alter the original column type. 00570 00571 We can't simply supply SEND_EOF flag to send_fields, because 00572 send_fields doesn't flush the network buffer. 00573 */ 00574 rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS); 00575 thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; 00576 result->send_eof(); 00577 thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; 00578 } 00579 return rc; 00580 } 00581 00582 00583 /* 00584 Fetch up to the given number of rows from a materialized cursor. 00585 00586 DESCRIPTION 00587 Precondition: the cursor is open. 00588 00589 If the cursor points after the last row, the fetch will automatically 00590 close the cursor and not send any data (except the 'EOF' packet 00591 with SERVER_STATUS_LAST_ROW_SENT). This is an extra round trip 00592 and probably should be improved to return 00593 SERVER_STATUS_LAST_ROW_SENT along with the last row. 00594 00595 RETURN VALUE 00596 none, in case of success the row is sent to the client, otherwise 00597 an error message is set in THD 00598 */ 00599 00600 void Materialized_cursor::fetch(ulong num_rows) 00601 { 00602 THD *thd= table->in_use; 00603 00604 int res= 0; 00605 result->begin_dataset(); 00606 for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++) 00607 { 00608 if ((res= table->file->rnd_next(table->record[0]))) 00609 break; 00610 /* Send data only if the read was successful. */ 00611 result->send_data(item_list); 00612 } 00613 00614 switch (res) { 00615 case 0: 00616 thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; 00617 result->send_eof(); 00618 thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; 00619 break; 00620 case HA_ERR_END_OF_FILE: 00621 thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; 00622 result->send_eof(); 00623 thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; 00624 close(); 00625 break; 00626 default: 00627 table->file->print_error(res, MYF(0)); 00628 close(); 00629 break; 00630 } 00631 } 00632 00633 00634 void Materialized_cursor::close() 00635 { 00636 /* Free item_list items */ 00637 free_items(); 00638 (void) table->file->ha_rnd_end(); 00639 /* 00640 We need to grab table->mem_root to prevent free_tmp_table from freeing: 00641 the cursor object was allocated in this memory. 00642 */ 00643 main_mem_root= table->mem_root; 00644 mem_root= &main_mem_root; 00645 clear_alloc_root(&table->mem_root); 00646 free_tmp_table(table->in_use, table); 00647 table= 0; 00648 } 00649 00650 00651 Materialized_cursor::~Materialized_cursor() 00652 { 00653 if (is_open()) 00654 close(); 00655 } 00656 00657 00658 /*************************************************************************** 00659 Select_materialize 00660 ****************************************************************************/ 00661 00662 bool Select_materialize::send_fields(List<Item> &list, uint flags) 00663 { 00664 DBUG_ASSERT(table == 0); 00665 if (create_result_table(unit->thd, unit->get_unit_column_types(), 00666 FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, "")) 00667 return TRUE; 00668 return FALSE; 00669 } 00670
1.4.7

