MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_cursor.cc
1 /* Copyright (c) 2005, 2012, 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 Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 #include "sql_priv.h"
17 #include "unireg.h"
18 #include "sql_cursor.h"
19 #include "probes_mysql.h"
20 #include "sql_parse.h" // mysql_execute_command
21 #include "sql_tmp_table.h" // tmp tables
22 
23 /****************************************************************************
24  Declarations.
25 ****************************************************************************/
26 
35 {
36  MEM_ROOT main_mem_root;
37  /* A fake unit to supply to select_send when fetching */
38  SELECT_LEX_UNIT fake_unit;
39  TABLE *table;
40  List<Item> item_list;
41  ulong fetch_limit;
42  ulong fetch_count;
43  bool is_rnd_inited;
44 public:
45  Materialized_cursor(select_result *result, TABLE *table);
46 
48  virtual bool is_open() const { return table != 0; }
49  virtual int open(JOIN *join __attribute__((unused)));
50  virtual void fetch(ulong num_rows);
51  virtual void close();
52  virtual ~Materialized_cursor();
53 };
54 
55 
65 class Select_materialize: public select_union
66 {
67  select_result *result;
68 public:
69  Materialized_cursor *materialized_cursor;
70  Select_materialize(select_result *result_arg)
71  :result(result_arg), materialized_cursor(0) {}
72  virtual bool send_result_set_metadata(List<Item> &list, uint flags);
73 };
74 
75 
76 /**************************************************************************/
77 
94 bool mysql_open_cursor(THD *thd, select_result *result,
95  Server_side_cursor **pcursor)
96 {
97  PSI_statement_locker *parent_locker;
98  select_result *save_result;
99  Select_materialize *result_materialize;
100  LEX *lex= thd->lex;
101 
102  if (! (result_materialize= new (thd->mem_root) Select_materialize(result)))
103  return true;
104 
105  save_result= lex->result;
106 
107  lex->result= result_materialize;
108 
109  MYSQL_QUERY_EXEC_START(thd->query(),
110  thd->thread_id,
111  (char *) (thd->db ? thd->db : ""),
112  &thd->security_ctx->priv_user[0],
113  (char *) thd->security_ctx->host_or_ip,
114  2);
115  parent_locker= thd->m_statement_psi;
116  thd->m_statement_psi= NULL;
117  bool rc= mysql_execute_command(thd);
118  thd->m_statement_psi= parent_locker;
119  MYSQL_QUERY_EXEC_DONE(rc);
120 
121  lex->result= save_result;
122  /*
123  Possible options here:
124  - a materialized cursor is open. In this case rc is 0 and
125  result_materialize->materialized is not NULL
126  - an error occurred during materialization.
127  result_materialize->materialized_cursor is not NULL, but rc != 0
128  - successful completion of mysql_execute_command without
129  a cursor: rc is 0, result_materialize->materialized_cursor is NULL.
130  This is possible if some command writes directly to the
131  network, bypassing select_result mechanism. An example of
132  such command is SHOW VARIABLES or SHOW STATUS.
133  */
134  if (rc)
135  {
136  if (result_materialize->materialized_cursor)
137  {
138  /* Rollback metadata in the client-server protocol. */
139  result_materialize->abort_result_set();
140 
141  delete result_materialize->materialized_cursor;
142  }
143 
144  goto end;
145  }
146 
147  if (result_materialize->materialized_cursor)
148  {
149  Materialized_cursor *materialized_cursor=
150  result_materialize->materialized_cursor;
151 
152  /*
153  NOTE: close_thread_tables() has been called in
154  mysql_execute_command(), so all tables except from the cursor
155  temporary table have been closed.
156  */
157 
158  if ((rc= materialized_cursor->open(0)))
159  {
160  delete materialized_cursor;
161  goto end;
162  }
163 
164  *pcursor= materialized_cursor;
165  thd->stmt_arena->cleanup_stmt();
166  }
167 
168 end:
169  delete result_materialize;
170  return rc;
171 }
172 
173 /****************************************************************************
174  Server_side_cursor
175 ****************************************************************************/
176 
177 Server_side_cursor::~Server_side_cursor()
178 {
179 }
180 
181 
182 void Server_side_cursor::operator delete(void *ptr, size_t size)
183 {
185  MEM_ROOT own_root= *cursor->mem_root;
186 
187  DBUG_ENTER("Server_side_cursor::operator delete");
188  TRASH(ptr, size);
189  /*
190  If this cursor has never been opened mem_root is empty. Otherwise
191  mem_root points to the memory the cursor object was allocated in.
192  In this case it's important to call free_root last, and free a copy
193  instead of *mem_root to avoid writing into freed memory.
194  */
195  free_root(&own_root, MYF(0));
196  DBUG_VOID_RETURN;
197 }
198 
199 
200 /***************************************************************************
201  Materialized_cursor
202 ****************************************************************************/
203 
204 Materialized_cursor::Materialized_cursor(select_result *result_arg,
205  TABLE *table_arg)
206  :Server_side_cursor(&table_arg->mem_root, result_arg),
207  table(table_arg),
208  fetch_limit(0),
209  fetch_count(0),
210  is_rnd_inited(0)
211 {
212  fake_unit.init_query();
213  fake_unit.thd= table->in_use;
214 }
215 
216 
227  THD *thd, List<Item> &send_result_set_metadata)
228 {
229  Query_arena backup_arena;
230  int rc;
231  List_iterator_fast<Item> it_org(send_result_set_metadata);
232  List_iterator_fast<Item> it_dst(item_list);
233  Item *item_org;
234  Item *item_dst;
235 
236  thd->set_n_backup_active_arena(this, &backup_arena);
237 
238  if ((rc= table->fill_item_list(&item_list)))
239  goto end;
240 
241  DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements);
242 
243  /*
244  Unless we preserve the original metadata, it will be lost,
245  since new fields describe columns of the temporary table.
246  Allocate a copy of the name for safety only. Currently
247  items with original names are always kept in memory,
248  but in case this changes a memory leak may be hard to notice.
249  */
250  while ((item_dst= it_dst++, item_org= it_org++))
251  {
252  Send_field send_field;
253  Item_ident *ident= static_cast<Item_ident *>(item_dst);
254  item_org->make_field(&send_field);
255 
256  ident->db_name= thd->strdup(send_field.db_name);
257  ident->table_name= thd->strdup(send_field.table_name);
258  }
259 
260  /*
261  Original metadata result set should be sent here. After
262  mysql_execute_command() is finished, item_list can not be used for
263  sending metadata, because it references closed table.
264  */
265  rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
266 
267 end:
268  thd->restore_active_arena(this, &backup_arena);
269  /* Check for thd->is_error() in case of OOM */
270  return rc || thd->is_error();
271 }
272 
273 
274 int Materialized_cursor::open(JOIN *join __attribute__((unused)))
275 {
276  THD *thd= fake_unit.thd;
277  int rc;
278  Query_arena backup_arena;
279 
280  thd->set_n_backup_active_arena(this, &backup_arena);
281 
282  /* Create a list of fields and start sequential scan. */
283 
284  rc= result->prepare(item_list, &fake_unit);
285  rc= !rc && table->file->ha_rnd_init(TRUE);
286  is_rnd_inited= !rc;
287 
288  thd->restore_active_arena(this, &backup_arena);
289 
290  /* Commit or rollback metadata in the client-server protocol. */
291 
292  if (!rc)
293  {
294  thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
295  result->send_eof();
296  }
297  else
298  {
299  result->abort_result_set();
300  }
301 
302  return rc;
303 }
304 
305 
318 void Materialized_cursor::fetch(ulong num_rows)
319 {
320  THD *thd= table->in_use;
321 
322  int res= 0;
323  result->begin_dataset();
324  for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++)
325  {
326  if ((res= table->file->ha_rnd_next(table->record[0])))
327  break;
328  /* Send data only if the read was successful. */
329  /*
330  If network write failed (i.e. due to a closed socked),
331  the error has already been set. Just return.
332  */
333  if (result->send_data(item_list))
334  return;
335  }
336 
337  switch (res) {
338  case 0:
339  thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
340  result->send_eof();
341  break;
342  case HA_ERR_END_OF_FILE:
343  thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
344  result->send_eof();
345  close();
346  break;
347  default:
348  table->file->print_error(res, MYF(0));
349  close();
350  break;
351  }
352 }
353 
354 
355 void Materialized_cursor::close()
356 {
357  /* Free item_list items */
358  free_items();
359  if (is_rnd_inited)
360  (void) table->file->ha_rnd_end();
361  /*
362  We need to grab table->mem_root to prevent free_tmp_table from freeing:
363  the cursor object was allocated in this memory.
364  */
365  main_mem_root= table->mem_root;
366  mem_root= &main_mem_root;
367  clear_alloc_root(&table->mem_root);
368  free_tmp_table(table->in_use, table);
369  table= 0;
370 }
371 
372 
373 Materialized_cursor::~Materialized_cursor()
374 {
375  if (is_open())
376  close();
377 }
378 
379 
380 /***************************************************************************
381  Select_materialize
382 ****************************************************************************/
383 
384 bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
385 {
386  DBUG_ASSERT(table == 0);
387  /*
388  PROCEDURE ANALYSE installs a result filter that has a different set
389  of input and output column Items:
390  */
391  List<Item> *column_types= (unit->first_select()->parent_lex->proc_analyse ?
392  &list : unit->get_field_list());
393  if (create_result_table(unit->thd, column_types,
394  FALSE,
395  thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS,
396  "", FALSE, TRUE))
397  return TRUE;
398 
399  materialized_cursor= new (&table->mem_root)
400  Materialized_cursor(result, table);
401 
402  if (!materialized_cursor)
403  {
404  free_tmp_table(table->in_use, table);
405  table= 0;
406  return TRUE;
407  }
408 
409  if (materialized_cursor->send_result_set_metadata(unit->thd, list))
410  {
411  delete materialized_cursor;
412  table= 0;
413  materialized_cursor= 0;
414  return TRUE;
415  }
416 
417  return FALSE;
418 }
419