sql_view.cc 52.7 KB
Newer Older
unknown's avatar
VIEW  
unknown committed
1 2 3 4
/* Copyright (C) 2004 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
VIEW  
unknown committed
6 7 8 9 10 11 12 13 14 15 16

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

17
#define MYSQL_LEX 1
unknown's avatar
VIEW  
unknown committed
18 19 20
#include "mysql_priv.h"
#include "sql_select.h"
#include "parse_file.h"
unknown's avatar
unknown committed
21
#include "sp.h"
22
#include "sp_head.h"
unknown's avatar
unknown committed
23
#include "sp_cache.h"
unknown's avatar
VIEW  
unknown committed
24

25 26
#define MD5_BUFF_LENGTH 33

unknown's avatar
unknown committed
27
const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") };
28

unknown's avatar
VIEW  
unknown committed
29 30 31
static int mysql_register_view(THD *thd, TABLE_LIST *view,
			       enum_view_create_mode mode);

32 33
const char *updatable_views_with_limit_names[]= { "NO", "YES", NullS };
TYPELIB updatable_views_with_limit_typelib=
unknown's avatar
VIEW  
unknown committed
34
{
35
  array_elements(updatable_views_with_limit_names)-1, "",
unknown's avatar
unknown committed
36 37
  updatable_views_with_limit_names,
  0
unknown's avatar
VIEW  
unknown committed
38 39 40
};


41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/*
  Make a unique name for an anonymous view column
  SYNOPSIS
    target        reference to the item for which a new name has to be made
    item_list     list of items within which we should check uniqueness of
                  the created name
    last_element  the last element of the list above

  NOTE
    Unique names are generated by adding 'My_exp_' to the old name of the
    column. In case the name that was created this way already exists, we
    add a numeric postfix to its end (i.e. "1") and increase the number
    until the name becomes unique. If the generated name is longer than
    NAME_LEN, it is truncated.
*/

static void make_unique_view_field_name(Item *target,
                                        List<Item> &item_list,
                                        Item *last_element)
{
  char *name= (target->orig_name ?
               target->orig_name :
               target->name);
unknown's avatar
unknown committed
64
  uint name_len, attempt;
65
  char buff[NAME_LEN+1];
unknown's avatar
unknown committed
66 67 68
  List_iterator_fast<Item> itc(item_list);

  for (attempt= 0;; attempt++)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  {
    Item *check;
    bool ok= TRUE;

    if (attempt)
      name_len= my_snprintf(buff, NAME_LEN, "My_exp_%d_%s", attempt, name);
    else
      name_len= my_snprintf(buff, NAME_LEN, "My_exp_%s", name);

    do
    {
      check= itc++;
      if (check != target &&
          my_strcasecmp(system_charset_info, buff, check->name) == 0)
      {
        ok= FALSE;
        break;
      }
    } while (check != last_element);
    if (ok)
      break;
unknown's avatar
unknown committed
90
    itc.rewind();
91 92 93 94 95 96
  }

  target->orig_name= target->name;
  target->set_name(buff, name_len, system_charset_info);
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

/*
  Check if items with same names are present in list and possibly
  generate unique names for them.

  SYNOPSIS
    item_list             list of Items which should be checked for duplicates
    gen_unique_view_name  flag: generate unique name or return with error when
                          duplicate names are found.

  DESCRIPTION
    This function is used on view creation and preparation of derived tables.
    It checks item_list for items with duplicate names. If it founds two
    items with same name and conversion to unique names isn't allowed, or
    names for both items are set by user - function fails.
    Otherwise it generates unique name for one item with autogenerated name
    using make_unique_view_field_name()

  RETURN VALUE
    FALSE no duplicate names found, or they are converted to unique ones
    TRUE  duplicate names are found and they can't be converted or conversion
          isn't allowed
*/

bool check_duplicate_names(List<Item> &item_list, bool gen_unique_view_name)
{
123 124 125
  Item *item;
  List_iterator_fast<Item> it(item_list);
  List_iterator_fast<Item> itc(item_list);
126
  DBUG_ENTER("check_duplicate_names");
127 128

  while ((item= it++))
129
  {
130 131 132 133 134 135
    Item *check;
    /* treat underlying fields like set by user names */
    if (item->real_item()->type() == Item::FIELD_ITEM)
      item->is_autogenerated_name= FALSE;
    itc.rewind();
    while ((check= itc++) && check != item)
136
    {
137
      if (my_strcasecmp(system_charset_info, item->name, check->name) == 0)
138
      {
139 140 141 142 143 144 145 146
        if (!gen_unique_view_name)
          goto err;
        if (item->is_autogenerated_name)
          make_unique_view_field_name(item, item_list, item);
        else if (check->is_autogenerated_name)
          make_unique_view_field_name(check, item_list, item);
        else
          goto err;
147 148 149 150
      }
    }
  }
  DBUG_RETURN(FALSE);
151 152 153 154

err:
  my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
  DBUG_RETURN(TRUE);
155 156
}

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
/*
  Fill defined view parts

  SYNOPSIS
    fill_defined_view_parts()
      thd                current thread.
      view               view to operate on

  DESCRIPTION
    This function will initialize the parts of the view 
    definition that are not specified in ALTER VIEW
    to their values from CREATE VIEW.
    The view must be opened to get its definition.
    We use a copy of the view when opening because we want 
    to preserve the original view instance.

  RETURN VALUE
    TRUE                 can't open table
    FALSE                success
*/
static bool
fill_defined_view_parts (THD *thd, TABLE_LIST *view)
{
  LEX *lex= thd->lex;
  bool not_used;
  TABLE_LIST decoy;

  memcpy (&decoy, view, sizeof (TABLE_LIST));
185
  if (!open_table(thd, &decoy, thd->mem_root, &not_used, OPEN_VIEW_NO_PARSE) &&
186 187
      !decoy.view)
  {
unknown's avatar
unknown committed
188
    /* It's a table */
189 190
    return TRUE;
  }
unknown's avatar
unknown committed
191

192 193 194 195 196 197 198
  if (!lex->definer)
  {
    view->definer.host= decoy.definer.host;
    view->definer.user= decoy.definer.user;
    lex->definer= &view->definer;
  }
  if (lex->create_view_algorithm == VIEW_ALGORITHM_UNDEFINED)
199
    lex->create_view_algorithm= (uint8) decoy.algorithm;
200 201 202 203 204 205 206
  if (lex->create_view_suid == VIEW_SUID_DEFAULT)
    lex->create_view_suid= decoy.view_suid ? 
      VIEW_SUID_DEFINER : VIEW_SUID_INVOKER;

  return FALSE;
}

207

unknown's avatar
VIEW  
unknown committed
208 209 210 211 212 213
/*
  Creating/altering VIEW procedure

  SYNOPSIS
    mysql_create_view()
    thd		- thread handler
214
    views	- views to create
unknown's avatar
VIEW  
unknown committed
215 216 217
    mode	- VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE

  RETURN VALUE
unknown's avatar
unknown committed
218 219
     FALSE OK
     TRUE  Error
unknown's avatar
VIEW  
unknown committed
220
*/
221

222
bool mysql_create_view(THD *thd, TABLE_LIST *views, 
unknown's avatar
unknown committed
223
                       enum_view_create_mode mode)
unknown's avatar
VIEW  
unknown committed
224 225 226 227 228 229
{
  LEX *lex= thd->lex;
  bool link_to_local;
  /* first table in list is target VIEW name => cut off it */
  TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
  TABLE_LIST *tables= lex->query_tables;
unknown's avatar
unknown committed
230
  TABLE_LIST *tbl;
unknown's avatar
unknown committed
231 232 233 234
  SELECT_LEX *select_lex= &lex->select_lex;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  SELECT_LEX *sl;
#endif
unknown's avatar
VIEW  
unknown committed
235
  SELECT_LEX_UNIT *unit= &lex->unit;
unknown's avatar
unknown committed
236
  bool res= FALSE;
unknown's avatar
VIEW  
unknown committed
237 238
  DBUG_ENTER("mysql_create_view");

239 240 241
  /* This is ensured in the parser. */
  DBUG_ASSERT(!lex->proc_list.first && !lex->result &&
              !lex->param_list.elements && !lex->derived_tables);
unknown's avatar
VIEW  
unknown committed
242

unknown's avatar
unknown committed
243
  if (mode != VIEW_CREATE_NEW)
244 245 246 247 248 249 250
  {
    if (mode == VIEW_ALTER &&
        fill_defined_view_parts(thd, view))
    {
      res= TRUE;
      goto err;
    }
unknown's avatar
unknown committed
251
    sp_cache_invalidate();
252
  }
unknown's avatar
unknown committed
253

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  if (!lex->definer)
  {
    /*
      DEFINER-clause is missing; we have to create default definer in
      persistent arena to be PS/SP friendly.
    */

    Query_arena original_arena;
    Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);

    if (!(lex->definer= create_default_definer(thd)))
      res= TRUE;

    if (ps_arena)
      thd->restore_active_arena(ps_arena, &original_arena);

    if (res)
      goto err;
  }

unknown's avatar
VIEW  
unknown committed
274
#ifndef NO_EMBEDDED_ACCESS_CHECKS
275 276 277 278 279
  /*
    check definer of view:
      - same as current user
      - current user has SUPER_ACL
  */
280
  if (strcmp(lex->definer->user.str,
281
             thd->security_ctx->priv_user) != 0 ||
282
      my_strcasecmp(system_charset_info,
283
                    lex->definer->host.str,
284
                    thd->security_ctx->priv_host) != 0)
285
  {
286
    if (!(thd->security_ctx->master_access & SUPER_ACL))
287
    {
288
      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
289 290 291 292 293
      res= TRUE;
      goto err;
    }
    else
    {
294 295
      if (!is_acl_user(lex->definer->host.str,
                       lex->definer->user.str))
296 297 298 299
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                            ER_NO_SUCH_USER,
                            ER(ER_NO_SUCH_USER),
300 301
                            lex->definer->user.str,
                            lex->definer->host.str);
302 303 304
      }
    }
  }
305 306
  /*
    Privilege check for view creation:
unknown's avatar
unknown committed
307 308
    - user has CREATE VIEW privilege on view table
    - user has DROP privilege in case of ALTER VIEW or CREATE OR REPLACE
309
    VIEW
unknown's avatar
unknown committed
310
    - user has some (SELECT/UPDATE/INSERT/DELETE) privileges on columns of
311 312 313
    underlying tables used on top of SELECT list (because it can be
    (theoretically) updated, so it is enough to have UPDATE privilege on
    them, for example)
unknown's avatar
unknown committed
314
    - user has SELECT privilege on columns used in expressions of VIEW select
315 316 317 318
    - for columns of underly tables used on top of SELECT list also will be
    checked that we have not more privileges on correspondent column of view
    table (i.e. user will not get some privileges by view creation)
  */
319
  if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
320
                    0, 0, is_schema_db(view->db)) ||
321 322
       grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
      (mode != VIEW_CREATE_NEW &&
323
       (check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
324
                     0, 0, is_schema_db(view->db)) ||
325
        grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0))))
unknown's avatar
unknown committed
326 327 328 329
  {
    res= TRUE;
    goto err;
  }
330
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
331
  {
332
    for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
unknown's avatar
VIEW  
unknown committed
333
    {
334
      /*
335
        Ensure that we have some privileges on this table, more strict check
336 337
        will be done on column level after preparation,
      */
unknown's avatar
unknown committed
338
      if (check_some_access(thd, VIEW_ANY_ACL, tbl))
339
      {
340
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
341 342
                 "ANY", thd->security_ctx->priv_user,
                 thd->security_ctx->priv_host, tbl->table_name);
unknown's avatar
unknown committed
343 344
        res= TRUE;
        goto err;
345
      }
346 347 348 349
      /*
        Mark this table as a table which will be checked after the prepare
        phase
      */
350
      tbl->table_in_first_from_clause= 1;
unknown's avatar
VIEW  
unknown committed
351

352
      /*
353 354 355
        We need to check only SELECT_ACL for all normal fields, fields for
        which we need "any" (SELECT/UPDATE/INSERT/DELETE) privilege will be
        checked later
356 357 358
      */
      tbl->grant.want_privilege= SELECT_ACL;
      /*
359
        Make sure that all rights are loaded to the TABLE::grant field.
360

361
        tbl->table_name will be correct name of table because VIEWs are
362 363 364
        not opened yet.
      */
      fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
365
                                      tbl->table_name);
366
    }
unknown's avatar
VIEW  
unknown committed
367 368 369 370 371
  }

  if (&lex->select_lex != lex->all_selects_list)
  {
    /* check tables of subqueries */
unknown's avatar
unknown committed
372
    for (tbl= tables; tbl; tbl= tbl->next_global)
unknown's avatar
VIEW  
unknown committed
373 374 375 376
    {
      if (!tbl->table_in_first_from_clause)
      {
        if (check_access(thd, SELECT_ACL, tbl->db,
377
                         &tbl->grant.privilege, 0, 0, test(tbl->schema_table)) ||
unknown's avatar
VIEW  
unknown committed
378 379
            grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
        {
unknown's avatar
unknown committed
380
          res= TRUE;
unknown's avatar
VIEW  
unknown committed
381 382 383 384 385 386
          goto err;
        }
      }
    }
  }
  /*
387
    Mark fields for special privilege check ("any" privilege)
unknown's avatar
VIEW  
unknown committed
388
  */
389
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
390
  {
391
    List_iterator_fast<Item> it(sl->item_list);
unknown's avatar
VIEW  
unknown committed
392 393 394
    Item *item;
    while ((item= it++))
    {
395 396 397
      Item_field *field;
      if ((field= item->filed_for_view_update()))
        field->any_privileges= 1;
unknown's avatar
VIEW  
unknown committed
398 399 400 401
    }
  }
#endif

unknown's avatar
unknown committed
402
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
403 404 405 406
  {
    res= TRUE;
    goto err;
  }
unknown's avatar
VIEW  
unknown committed
407

408 409
  /*
    check that tables are not temporary  and this VIEW do not used in query
unknown's avatar
unknown committed
410 411 412 413
    (it is possible with ALTERing VIEW).
    open_and_lock_tables can change the value of tables,
    e.g. it may happen if before the function call tables was equal to 0. 
  */ 
414
  for (tbl= lex->query_tables; tbl; tbl= tbl->next_global)
unknown's avatar
VIEW  
unknown committed
415
  {
416 417 418
    /* is this table view and the same view which we creates now? */
    if (tbl->view &&
        strcmp(tbl->view_db.str, view->db) == 0 &&
419
        strcmp(tbl->view_name.str, view->table_name) == 0)
420 421
    {
      my_error(ER_NO_SUCH_TABLE, MYF(0), tbl->view_db.str, tbl->view_name.str);
unknown's avatar
unknown committed
422
      res= TRUE;
423 424 425
      goto err;
    }

unknown's avatar
VIEW  
unknown committed
426
    /*
427 428 429 430 431
      tbl->table can be NULL when tbl is a placeholder for a view
      that is indirectly referenced via a stored function from the
      view being created. We don't check these indirectly
      referenced views in CREATE VIEW so they don't have table
      object.
unknown's avatar
VIEW  
unknown committed
432
    */
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    if (tbl->table)
    {
      /* is this table temporary and is not view? */
      if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view &&
          !tbl->schema_table)
      {
        my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
        res= TRUE;
        goto err;
      }
      /*
        Copy the privileges of the underlying VIEWs which were filled by
        fill_effective_table_privileges
        (they were not copied at derived tables processing)
      */
      tbl->table->grant.privilege= tbl->grant.privilege;
    }
unknown's avatar
VIEW  
unknown committed
450 451
  }

452
  /* prepare select to resolve all fields */
unknown's avatar
VIEW  
unknown committed
453
  lex->view_prepare_mode= 1;
454
  if (unit->prepare(thd, 0, 0))
455 456 457 458 459
  {
    /*
      some errors from prepare are reported to user, if is not then
      it will be checked after err: label
    */
unknown's avatar
unknown committed
460
    res= TRUE;
unknown's avatar
VIEW  
unknown committed
461
    goto err;
462
  }
unknown's avatar
VIEW  
unknown committed
463 464 465 466 467 468 469 470

  /* view list (list of view fields names) */
  if (lex->view_list.elements)
  {
    List_iterator_fast<Item> it(select_lex->item_list);
    List_iterator_fast<LEX_STRING> nm(lex->view_list);
    Item *item;
    LEX_STRING *name;
471 472

    if (lex->view_list.elements != select_lex->item_list.elements)
unknown's avatar
VIEW  
unknown committed
473
    {
474
      my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0));
475
      res= TRUE;
476
      goto err;
unknown's avatar
VIEW  
unknown committed
477
    }
478
    while ((item= it++, name= nm++))
479
    {
480
      item->set_name(name->str, name->length, system_charset_info);
481 482
      item->is_autogenerated_name= FALSE;
    }
unknown's avatar
VIEW  
unknown committed
483 484
  }

485
  if (check_duplicate_names(select_lex->item_list, 1))
unknown's avatar
unknown committed
486 487 488 489
  {
    res= TRUE;
    goto err;
  }
490

unknown's avatar
VIEW  
unknown committed
491 492
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  /*
493
    Compare/check grants on view with grants of underlying tables
unknown's avatar
VIEW  
unknown committed
494
  */
495
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
496
  {
unknown's avatar
unknown committed
497
    DBUG_ASSERT(view->db);                     /* Must be set in the parser */
498
    List_iterator_fast<Item> it(sl->item_list);
unknown's avatar
VIEW  
unknown committed
499
    Item *item;
unknown's avatar
unknown committed
500
    fill_effective_table_privileges(thd, &view->grant, view->db,
501
                                    view->table_name);
502
    while ((item= it++))
unknown's avatar
VIEW  
unknown committed
503
    {
504
      Item_field *fld;
unknown's avatar
unknown committed
505
      uint priv= (get_column_grant(thd, &view->grant, view->db,
506
                                    view->table_name, item->name) &
unknown's avatar
VIEW  
unknown committed
507
                  VIEW_ANY_ACL);
508
      if ((fld= item->filed_for_view_update()))
unknown's avatar
VIEW  
unknown committed
509 510
      {
        /*
511
          Do we have more privileges on view field then underlying table field?
unknown's avatar
VIEW  
unknown committed
512
        */
513
        if (!fld->field->table->s->tmp_table && (~fld->have_privileges & priv))
unknown's avatar
VIEW  
unknown committed
514 515
        {
          /* VIEW column has more privileges */
516
          my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
517 518
                   "create view", thd->security_ctx->priv_user,
                   thd->security_ctx->priv_host, item->name,
519
                   view->table_name);
unknown's avatar
unknown committed
520 521
          res= TRUE;
          goto err;
unknown's avatar
VIEW  
unknown committed
522 523 524 525 526 527
        }
      }
    }
  }
#endif

528
  if (wait_if_global_read_lock(thd, 0, 0))
unknown's avatar
VIEW  
unknown committed
529
  {
unknown's avatar
unknown committed
530
    res= TRUE;
unknown's avatar
VIEW  
unknown committed
531 532 533
    goto err;
  }
  VOID(pthread_mutex_lock(&LOCK_open));
534
  res= mysql_register_view(thd, view, mode);
535 536 537 538 539

  if (mysql_bin_log.is_open())
  {
    String buff;
    const LEX_STRING command[3]=
540 541 542
      {{ C_STRING_WITH_LEN("CREATE ") },
       { C_STRING_WITH_LEN("ALTER ") },
       { C_STRING_WITH_LEN("CREATE OR REPLACE ") }};
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557

    buff.append(command[thd->lex->create_view_mode].str,
                command[thd->lex->create_view_mode].length);
    view_store_options(thd, views, &buff);
    buff.append(STRING_WITH_LEN("VIEW "));
    /* Test if user supplied a db (ie: we did not use thd->db) */
    if (views->db && views->db[0] &&
        (thd->db == NULL || strcmp(views->db, thd->db)))
    {
      append_identifier(thd, &buff, views->db,
                        views->db_length);
      buff.append('.');
    }
    append_identifier(thd, &buff, views->table_name,
                      views->table_name_length);
558 559 560 561 562 563
    if (lex->view_list.elements)
    {
      List_iterator_fast<LEX_STRING> names(lex->view_list);
      LEX_STRING *name;
      int i;
      
unknown's avatar
unknown committed
564
      for (i= 0; (name= names++); i++)
565 566 567 568 569 570
      {
        buff.append(i ? ", " : "(");
        append_identifier(thd, &buff, name->str, name->length);
      }
      buff.append(')');
    }
571 572 573
    buff.append(STRING_WITH_LEN(" AS "));
    buff.append(views->source.str, views->source.length);

574 575
    thd->binlog_query(THD::STMT_QUERY_TYPE,
                      buff.ptr(), buff.length(), FALSE, FALSE);
576 577
  }

unknown's avatar
VIEW  
unknown committed
578
  VOID(pthread_mutex_unlock(&LOCK_open));
579 580
  if (view->revision != 1)
    query_cache_invalidate3(thd, view, 0);
unknown's avatar
VIEW  
unknown committed
581
  start_waiting_global_read_lock(thd);
582 583
  if (res)
    goto err;
unknown's avatar
VIEW  
unknown committed
584 585 586

  send_ok(thd);
  lex->link_first_table_back(view, link_to_local);
unknown's avatar
unknown committed
587
  DBUG_RETURN(0);
unknown's avatar
VIEW  
unknown committed
588 589 590 591 592

err:
  thd->proc_info= "end";
  lex->link_first_table_back(view, link_to_local);
  unit->cleanup();
unknown's avatar
unknown committed
593
  DBUG_RETURN(res || thd->net.report_error);
unknown's avatar
VIEW  
unknown committed
594 595 596
}


597
/* index of revision number in following table */
598
static const int revision_number_position= 8;
599
/* index of last required parameter for making view */
600
static const int required_view_parameters= 10;
601 602
/* number of backups */
static const int num_view_backups= 3;
unknown's avatar
VIEW  
unknown committed
603

604 605 606 607 608 609
/*
  table of VIEW .frm field descriptors

  Note that one should NOT change the order for this, as it's used by
  parse()
*/
unknown's avatar
VIEW  
unknown committed
610
static File_option view_parameters[]=
unknown's avatar
unknown committed
611
{{{ C_STRING_WITH_LEN("query")},
612
  my_offsetof(TABLE_LIST, query),
613
  FILE_OPTIONS_ESTRING},
unknown's avatar
unknown committed
614
 {{ C_STRING_WITH_LEN("md5")},
615
  my_offsetof(TABLE_LIST, md5),
616
  FILE_OPTIONS_STRING},
unknown's avatar
unknown committed
617
 {{ C_STRING_WITH_LEN("updatable")},
618
  my_offsetof(TABLE_LIST, updatable_view),
619
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
620
 {{ C_STRING_WITH_LEN("algorithm")},
621
  my_offsetof(TABLE_LIST, algorithm),
622
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
623
 {{ C_STRING_WITH_LEN("definer_user")},
624
  my_offsetof(TABLE_LIST, definer.user),
unknown's avatar
VIEW  
unknown committed
625
  FILE_OPTIONS_STRING},
unknown's avatar
unknown committed
626
 {{ C_STRING_WITH_LEN("definer_host")},
627
  my_offsetof(TABLE_LIST, definer.host),
unknown's avatar
VIEW  
unknown committed
628
  FILE_OPTIONS_STRING},
unknown's avatar
unknown committed
629
 {{ C_STRING_WITH_LEN("suid")},
630
  my_offsetof(TABLE_LIST, view_suid),
unknown's avatar
VIEW  
unknown committed
631
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
632
 {{ C_STRING_WITH_LEN("with_check_option")},
633
  my_offsetof(TABLE_LIST, with_check),
unknown's avatar
VIEW  
unknown committed
634
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
635
 {{ C_STRING_WITH_LEN("revision")},
636
  my_offsetof(TABLE_LIST, revision),
unknown's avatar
VIEW  
unknown committed
637
  FILE_OPTIONS_REV},
unknown's avatar
unknown committed
638
 {{ C_STRING_WITH_LEN("timestamp")},
639
  my_offsetof(TABLE_LIST, timestamp),
unknown's avatar
VIEW  
unknown committed
640
  FILE_OPTIONS_TIMESTAMP},
unknown's avatar
unknown committed
641
 {{ C_STRING_WITH_LEN("create-version")},
642
  my_offsetof(TABLE_LIST, file_version),
unknown's avatar
VIEW  
unknown committed
643
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
644
 {{ C_STRING_WITH_LEN("source")},
645
  my_offsetof(TABLE_LIST, source),
unknown's avatar
VIEW  
unknown committed
646
  FILE_OPTIONS_ESTRING},
unknown's avatar
unknown committed
647
 {{NullS, 0},			0,
unknown's avatar
VIEW  
unknown committed
648 649 650
  FILE_OPTIONS_STRING}
};

651
static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
unknown's avatar
VIEW  
unknown committed
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667


/*
  Register VIEW (write .frm & process .frm's history backups)

  SYNOPSIS
    mysql_register_view()
    thd		- thread handler
    view	- view description
    mode	- VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE

  RETURN
     0	OK
    -1	Error
     1	Error and error message given
*/
668

unknown's avatar
VIEW  
unknown committed
669 670 671
static int mysql_register_view(THD *thd, TABLE_LIST *view,
			       enum_view_create_mode mode)
{
672
  LEX *lex= thd->lex;
unknown's avatar
VIEW  
unknown committed
673 674
  char buff[4096];
  String str(buff,(uint32) sizeof(buff), system_charset_info);
675
  char md5[MD5_BUFF_LENGTH];
unknown's avatar
VIEW  
unknown committed
676
  bool can_be_merged;
677
  char dir_buff[FN_REFLEN], path_buff[FN_REFLEN];
678
  const uchar *endp;
679
  LEX_STRING dir, file, path;
unknown's avatar
VIEW  
unknown committed
680 681
  DBUG_ENTER("mysql_register_view");

682
  /* print query */
unknown's avatar
VIEW  
unknown committed
683
  str.length(0);
684 685 686
  {
    ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
    thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
687
    lex->unit.print(&str);
688 689
    thd->variables.sql_mode|= sql_mode;
  }
unknown's avatar
VIEW  
unknown committed
690
  str.append('\0');
691
  DBUG_PRINT("info", ("View: %s", str.ptr()));
unknown's avatar
VIEW  
unknown committed
692

693
  /* print file name */
694
  dir.length= build_table_filename(dir_buff, sizeof(dir_buff),
695
                                   view->db, "", "", 0);
unknown's avatar
VIEW  
unknown committed
696 697
  dir.str= dir_buff;

698
  path.length= build_table_filename(path_buff, sizeof(path_buff),
699
                                    view->db, view->table_name, reg_ext, 0);
700 701 702 703 704
  path.str= path_buff;

  file.str= path.str + dir.length;
  file.length= path.length - dir.length;

unknown's avatar
VIEW  
unknown committed
705
  /* init timestamp */
706
  if (!view->timestamp.str)
unknown's avatar
VIEW  
unknown committed
707 708
    view->timestamp.str= view->timestamp_buffer;

709
  /* check old .frm */
unknown's avatar
VIEW  
unknown committed
710 711 712
  {
    char path_buff[FN_REFLEN];
    LEX_STRING path;
713
    File_parser *parser;
unknown's avatar
unknown committed
714

unknown's avatar
VIEW  
unknown committed
715 716 717 718 719 720 721 722 723
    path.str= path_buff;
    fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME);
    path.length= strlen(path_buff);

    if (!access(path.str, F_OK))
    {
      if (mode == VIEW_CREATE_NEW)
      {
	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias);
unknown's avatar
unknown committed
724
	DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
725 726
      }

unknown's avatar
unknown committed
727
      if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0)))
728 729
	DBUG_RETURN(1);

730
      if (!parser->ok() || !is_equal(&view_type, parser->type()))
unknown's avatar
VIEW  
unknown committed
731
      {
unknown's avatar
unknown committed
732
        my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
unknown's avatar
merge  
unknown committed
733
        DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
734
      }
735 736 737

      /*
        read revision number
738

739
        TODO: read dependence list, too, to process cascade/restrict
740 741
        TODO: special cascade/restrict procedure for alter?
      */
unknown's avatar
unknown committed
742
      if (parser->parse((gptr)view, thd->mem_root,
743 744
                        view_parameters + revision_number_position, 1,
                        &file_parser_dummy_hook))
unknown's avatar
VIEW  
unknown committed
745
      {
unknown's avatar
merge  
unknown committed
746
        DBUG_RETURN(thd->net.report_error? -1 : 0);
unknown's avatar
VIEW  
unknown committed
747 748 749 750 751 752 753
      }
    }
    else
    {
      if (mode == VIEW_ALTER)
      {
	my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias);
unknown's avatar
unknown committed
754
	DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
755 756 757
      }
    }
  }
758
  /* fill structure */
unknown's avatar
VIEW  
unknown committed
759 760
  view->query.str= (char*)str.ptr();
  view->query.length= str.length()-1; // we do not need last \0
761
  view->source.str= thd->query + thd->lex->create_view_select_start;
762 763 764
  endp= (uchar*) view->source.str;
  endp= skip_rear_comments(endp, (uchar*) (thd->query + thd->query_length));
  view->source.length= endp - (uchar*) view->source.str;
unknown's avatar
VIEW  
unknown committed
765 766 767 768
  view->file_version= 1;
  view->calc_md5(md5);
  view->md5.str= md5;
  view->md5.length= 32;
769 770 771
  can_be_merged= lex->can_be_merged();
  if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
      !lex->can_be_merged())
unknown's avatar
VIEW  
unknown committed
772 773 774
  {
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
                 ER(ER_WARN_VIEW_MERGE));
775
    lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
unknown's avatar
VIEW  
unknown committed
776
  }
777
  view->algorithm= lex->create_view_algorithm;
778 779
  view->definer.user= lex->definer->user;
  view->definer.host= lex->definer->host;
780
  view->view_suid= lex->create_view_suid;
781
  view->with_check= lex->create_view_check;
782
  if ((view->updatable_view= (can_be_merged &&
unknown's avatar
unknown committed
783
                              view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
unknown's avatar
VIEW  
unknown committed
784
  {
785
    /* TODO: change here when we will support UNIONs */
786
    for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first;
787 788
	 tbl;
	 tbl= tbl->next_local)
unknown's avatar
VIEW  
unknown committed
789
    {
790
      if ((tbl->view && !tbl->updatable_view) || tbl->schema_table)
unknown's avatar
unknown committed
791
      {
792 793 794 795
	view->updatable_view= 0;
	break;
      }
      for (TABLE_LIST *up= tbl; up; up= up->embedding)
unknown's avatar
VIEW  
unknown committed
796
      {
797 798 799 800 801
	if (up->outer_join)
	{
	  view->updatable_view= 0;
	  goto loop_out;
	}
unknown's avatar
VIEW  
unknown committed
802 803 804
      }
    }
  }
805
loop_out:
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
  /*
    Check that table of main select do not used in subqueries.

    This test can catch only very simple cases of such non-updateable views,
    all other will be detected before updating commands execution.
    (it is more optimisation then real check)

    NOTE: this skip cases of using table via VIEWs, joined VIEWs, VIEWs with
    UNION
  */
  if (view->updatable_view &&
      !lex->select_lex.next_select() &&
      !((TABLE_LIST*)lex->select_lex.table_list.first)->next_local &&
      find_table_in_global_list(lex->query_tables->next_global,
				lex->query_tables->db,
821
				lex->query_tables->table_name))
822 823 824 825
  {
    view->updatable_view= 0;
  }

unknown's avatar
unknown committed
826 827 828
  if (view->with_check != VIEW_CHECK_NONE &&
      !view->updatable_view)
  {
829
    my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name);
unknown's avatar
unknown committed
830 831 832
    DBUG_RETURN(-1);
  }

unknown's avatar
VIEW  
unknown committed
833
  if (sql_create_definition_file(&dir, &file, view_file_type,
834
				 (gptr)view, view_parameters, num_view_backups))
unknown's avatar
VIEW  
unknown committed
835
  {
unknown's avatar
unknown committed
836
    DBUG_RETURN(thd->net.report_error? -1 : 1);
unknown's avatar
VIEW  
unknown committed
837 838 839 840 841
  }
  DBUG_RETURN(0);
}


842

unknown's avatar
VIEW  
unknown committed
843 844 845 846 847
/*
  read VIEW .frm and create structures

  SYNOPSIS
    mysql_make_view()
848 849 850
    thd			Thread handler
    parser		parser object
    table		TABLE_LIST structure for filling
851
    flags               flags
852
  RETURN
853 854
    0 ok
    1 error
unknown's avatar
VIEW  
unknown committed
855
*/
856

857 858
bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
                     uint flags)
unknown's avatar
VIEW  
unknown committed
859
{
860 861 862
  SELECT_LEX *end, *view_select;
  LEX *old_lex, *lex;
  Query_arena *arena, backup;
863
  TABLE_LIST *top_view= table->top_table();
864 865
  int res;
  bool result;
unknown's avatar
VIEW  
unknown committed
866
  DBUG_ENTER("mysql_make_view");
867
  DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name));
unknown's avatar
VIEW  
unknown committed
868 869 870

  if (table->view)
  {
871 872 873 874 875 876 877 878 879 880 881 882 883 884
    /*
      It's an execution of a PS/SP and the view has already been unfolded
      into a list of used tables. Now we only need to update the information
      about granted privileges in the view tables with the actual data
      stored in MySQL privilege system.  We don't need to restore the
      required privileges (by calling register_want_access) because they has
      not changed since PREPARE or the previous execution: the only case
      when this information is changed is execution of UPDATE on a view, but
      the original want_access is restored in its end.
    */
    if (!table->prelocking_placeholder && table->prepare_security(thd))
    {
      DBUG_RETURN(1);
    }
unknown's avatar
VIEW  
unknown committed
885
    DBUG_PRINT("info",
886
               ("VIEW %s.%s is already processed on previous PS/SP execution",
unknown's avatar
VIEW  
unknown committed
887 888 889 890
                table->view_db.str, table->view_name.str));
    DBUG_RETURN(0);
  }

891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
  /* check loop via view definition */
  for (TABLE_LIST *precedent= table->referencing_view;
       precedent;
       precedent= precedent->referencing_view)
  {
    if (precedent->view_name.length == table->table_name_length &&
        precedent->view_db.length == table->db_length &&
        my_strcasecmp(system_charset_info,
                      precedent->view_name.str, table->table_name) == 0 &&
        my_strcasecmp(system_charset_info,
                      precedent->view_db.str, table->db) == 0)
    {
      my_error(ER_VIEW_RECURSIVE, MYF(0),
               top_view->view_db.str, top_view->view_name.str);
      DBUG_RETURN(TRUE);
    }
  }

unknown's avatar
VIEW  
unknown committed
909 910 911 912
  /*
    For now we assume that tables will not be changed during PS life (it
    will be TRUE as far as we make new table cache).
  */
913 914
  old_lex= thd->lex;
  arena= thd->stmt_arena;
unknown's avatar
unknown committed
915
  if (arena->is_conventional())
916 917
    arena= 0;
  else
unknown's avatar
Rename:  
unknown committed
918
    thd->set_n_backup_active_arena(arena, &backup);
unknown's avatar
VIEW  
unknown committed
919 920

  /* init timestamp */
921
  if (!table->timestamp.str)
unknown's avatar
VIEW  
unknown committed
922
    table->timestamp.str= table->timestamp_buffer;
923
  /* prepare default values for old format */
924
  table->view_suid= TRUE;
925 926 927
  table->definer.user.str= table->definer.host.str= 0;
  table->definer.user.length= table->definer.host.length= 0;

unknown's avatar
VIEW  
unknown committed
928 929 930 931
  /*
    TODO: when VIEWs will be stored in cache, table mem_root should
    be used here
  */
unknown's avatar
unknown committed
932
  if (parser->parse((gptr)table, thd->mem_root, view_parameters,
933
                    required_view_parameters, &file_parser_dummy_hook))
unknown's avatar
VIEW  
unknown committed
934 935
    goto err;

936 937 938 939 940 941 942 943 944 945 946
  /*
    check old format view .frm
  */
  if (!table->definer.user.str)
  {
    DBUG_ASSERT(!table->definer.host.str &&
                !table->definer.user.length &&
                !table->definer.host.length);
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                        ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER),
                        table->db, table->table_name);
947
    get_default_definer(thd, &table->definer);
948
  }
949 950 951 952
  if (flags & OPEN_VIEW_NO_PARSE)
  {
    DBUG_RETURN(FALSE);
  }
953

unknown's avatar
VIEW  
unknown committed
954 955 956 957 958 959
  /*
    Save VIEW parameters, which will be wiped out by derived table
    processing
  */
  table->view_db.str= table->db;
  table->view_db.length= table->db_length;
960 961
  table->view_name.str= table->table_name;
  table->view_name.length= table->table_name_length;
unknown's avatar
VIEW  
unknown committed
962

unknown's avatar
unknown committed
963
  /*TODO: md5 test here and warning if it is differ */
unknown's avatar
VIEW  
unknown committed
964

unknown's avatar
unknown committed
965 966 967 968 969 970
  /*
    TODO: TABLE mem root should be used here when VIEW will be stored in
    TABLE cache

    now Lex placed in statement memory
  */
unknown's avatar
unknown committed
971
  table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
unknown's avatar
unknown committed
972
  lex_start(thd, (uchar*)table->query.str, table->query.length);
973
  view_select= &lex->select_lex;
974
  view_select->select_number= ++thd->select_number;
unknown's avatar
VIEW  
unknown committed
975
  {
976
    ulong save_mode= thd->variables.sql_mode;
unknown's avatar
VIEW  
unknown committed
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
    /* switch off modes which can prevent normal parsing of VIEW
      - MODE_REAL_AS_FLOAT            affect only CREATE TABLE parsing
      + MODE_PIPES_AS_CONCAT          affect expression parsing
      + MODE_ANSI_QUOTES              affect expression parsing
      + MODE_IGNORE_SPACE             affect expression parsing
      - MODE_NOT_USED                 not used :)
      * MODE_ONLY_FULL_GROUP_BY       affect execution
      * MODE_NO_UNSIGNED_SUBTRACTION  affect execution
      - MODE_NO_DIR_IN_CREATE         affect table creation only
      - MODE_POSTGRESQL               compounded from other modes
      - MODE_ORACLE                   compounded from other modes
      - MODE_MSSQL                    compounded from other modes
      - MODE_DB2                      compounded from other modes
      - MODE_MAXDB                    affect only CREATE TABLE parsing
      - MODE_NO_KEY_OPTIONS           affect only SHOW
      - MODE_NO_TABLE_OPTIONS         affect only SHOW
      - MODE_NO_FIELD_OPTIONS         affect only SHOW
      - MODE_MYSQL323                 affect only SHOW
      - MODE_MYSQL40                  affect only SHOW
      - MODE_ANSI                     compounded from other modes
                                      (+ transaction mode)
      ? MODE_NO_AUTO_VALUE_ON_ZERO    affect UPDATEs
      + MODE_NO_BACKSLASH_ESCAPES     affect expression parsing
    */
1001 1002
    thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
                                MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
1003
    CHARSET_INFO *save_cs= thd->variables.character_set_client;
unknown's avatar
unknown committed
1004
    thd->variables.character_set_client= system_charset_info;
1005
    res= MYSQLparse((void *)thd);
1006
    thd->variables.character_set_client= save_cs;
1007
    thd->variables.sql_mode= save_mode;
unknown's avatar
VIEW  
unknown committed
1008 1009 1010
  }
  if (!res && !thd->is_fatal_error)
  {
1011 1012
    TABLE_LIST *view_tables= lex->query_tables;
    TABLE_LIST *view_tables_tail= 0;
1013
    TABLE_LIST *tbl;
unknown's avatar
unknown committed
1014

unknown's avatar
VIEW  
unknown committed
1015
    /*
1016 1017 1018
      Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
      underlying tables.
      Skip this step if we are opening view for prelocking only.
unknown's avatar
VIEW  
unknown committed
1019
    */
1020 1021
    if (!table->prelocking_placeholder &&
        (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
unknown's avatar
VIEW  
unknown committed
1022
    {
1023
      if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
1024
          check_table_access(thd, SHOW_VIEW_ACL, table, 1))
unknown's avatar
VIEW  
unknown committed
1025
      {
unknown's avatar
unknown committed
1026
        my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
unknown's avatar
VIEW  
unknown committed
1027 1028 1029
        goto err;
      }
    }
1030
    else if (!table->prelocking_placeholder &&
1031 1032
             old_lex->sql_command == SQLCOM_SHOW_CREATE &&
             !table->belong_to_view)
1033 1034 1035 1036
    {
      if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
        goto err;
    }
unknown's avatar
VIEW  
unknown committed
1037

1038 1039 1040
    if (!(table->view_tables=
          (List<TABLE_LIST>*) new(thd->mem_root) List<TABLE_LIST>))
      goto err;
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
    /*
      mark to avoid temporary table using and put view reference and find
      last view table
    */
    for (tbl= view_tables;
         tbl;
         tbl= (view_tables_tail= tbl)->next_global)
    {
      tbl->skip_temporary= 1;
      tbl->belong_to_view= top_view;
1051
      tbl->referencing_view= table;
1052
      tbl->prelocking_placeholder= table->prelocking_placeholder;
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
      /*
        First we fill want_privilege with SELECT_ACL (this is needed for the
        tables which belongs to view subqueries and temporary table views,
        then for the merged view underlying tables we will set wanted
        privileges of top_view
      */
      tbl->grant.want_privilege= SELECT_ACL;
      /*
        After unfolding the view we lose the list of tables referenced in it
        (we will have only a list of underlying tables in case of MERGE
        algorithm, which does not include the tables referenced from
        subqueries used in view definition).
        Let's build a list of all tables referenced in the view.
      */
      table->view_tables->push_back(tbl);
1068 1069
    }

1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
    /*
      Put tables of VIEW after VIEW TABLE_LIST

      NOTE: It is important for UPDATE/INSERT/DELETE checks to have this
      tables just after VIEW instead of tail of list, to be able check that
      table is unique. Also we store old next table for the same purpose.
    */
    if (view_tables)
    {
      if (table->next_global)
      {
1081
        view_tables_tail->next_global= table->next_global;
1082 1083 1084 1085
        table->next_global->prev_global= &view_tables_tail->next_global;
      }
      else
      {
1086
        old_lex->query_tables_last= &view_tables_tail->next_global;
1087 1088 1089 1090 1091
      }
      view_tables->prev_global= &table->next_global;
      table->next_global= view_tables;
    }

unknown's avatar
unknown committed
1092 1093 1094 1095 1096 1097
    /*
      If the view's body needs row-based binlogging (e.g. the VIEW is created
      from SELECT UUID()), the top statement also needs it.
    */
    if (lex->binlog_row_based_if_mixed)
      old_lex->binlog_row_based_if_mixed= TRUE;
1098 1099 1100
    bool view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
                             lex->can_be_merged());
    TABLE_LIST *view_main_select_tables;
1101 1102
    LINT_INIT(view_main_select_tables);

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
    if (view_is_mergeable)
    {
      /*
        Currently 'view_main_select_tables' differs from 'view_tables'
        only then view has CONVERT_TZ() function in its select list.
        This may change in future, for example if we enable merging of
        views with subqueries in select list.
      */
      view_main_select_tables=
        (TABLE_LIST*)lex->select_lex.table_list.first;

      /*
        Let us set proper lock type for tables of the view's main
        select since we may want to perform update or insert on
        view. This won't work for view containing union. But this is
        ok since we don't allow insert and update on such views
        anyway.
      */
      for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
        tbl->lock_type= table->lock_type;
    }
unknown's avatar
unknown committed
1124

1125 1126 1127 1128 1129 1130 1131 1132
    /*
      If we are opening this view as part of implicit LOCK TABLES, then
      this view serves as simple placeholder and we should not continue
      further processing.
    */
    if (table->prelocking_placeholder)
      goto ok2;

1133
    old_lex->derived_tables|= (DERIVED_VIEW | lex->derived_tables);
1134 1135 1136 1137 1138 1139 1140 1141

    /* move SQL_NO_CACHE & Co to whole query */
    old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
				   lex->safe_to_cache_query);
    /* move SQL_CACHE to whole query */
    if (view_select->options & OPTION_TO_QUERY_CACHE)
      old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;

1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    if (table->view_suid)
    {
      /*
        Prepare a security context to check underlying objects of the view
      */
      Security_context *save_security_ctx= thd->security_ctx;
      if (!(table->view_sctx= (Security_context *)
            thd->stmt_arena->alloc(sizeof(Security_context))))
        goto err;
      /* Assign the context to the tables referenced in the view */
      for (tbl= view_tables; tbl; tbl= tbl->next_global)
        tbl->security_ctx= table->view_sctx;
      /* assign security context to SELECT name resolution contexts of view */
      for(SELECT_LEX *sl= lex->all_selects_list;
          sl;
          sl= sl->next_select_in_list())
        sl->context.security_ctx= table->view_sctx;
    }

    /*
      Setup an error processor to hide error messages issued by stored
      routines referenced in the view
    */
    for (SELECT_LEX *sl= lex->all_selects_list;
         sl;
         sl= sl->next_select_in_list())
    {
      sl->context.error_processor= &view_error_processor;
      sl->context.error_processor_data= (void *)table;
    }

unknown's avatar
VIEW  
unknown committed
1173 1174 1175
    /*
      check MERGE algorithm ability
      - algorithm is not explicit TEMPORARY TABLE
1176
      - VIEW SELECT allow merging
unknown's avatar
VIEW  
unknown committed
1177 1178
      - VIEW used in subquery or command support MERGE algorithm
    */
1179
    if (view_is_mergeable &&
unknown's avatar
VIEW  
unknown committed
1180
        (table->select_lex->master_unit() != &old_lex->unit ||
1181 1182
         old_lex->can_use_merged()) &&
        !old_lex->can_not_use_merged())
unknown's avatar
VIEW  
unknown committed
1183 1184
    {
      /* lex should contain at least one table */
1185
      DBUG_ASSERT(view_main_select_tables != 0);
unknown's avatar
VIEW  
unknown committed
1186

1187 1188
      List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);

unknown's avatar
VIEW  
unknown committed
1189
      table->effective_algorithm= VIEW_ALGORITHM_MERGE;
1190
      DBUG_PRINT("info", ("algorithm: MERGE"));
1191
      table->updatable= (table->updatable_view != 0);
1192 1193
      table->effective_with_check=
        old_lex->get_effective_with_check(table);
1194
      table->merge_underlying_list= view_main_select_tables;
1195

1196 1197
      /* Fill correct wanted privileges. */
      for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
1198
        tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
1199 1200

      /* prepare view context */
1201
      lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
1202 1203
      lex->select_lex.context.outer_context= 0;
      lex->select_lex.context.select_lex= table->select_lex;
unknown's avatar
unknown committed
1204 1205 1206
      lex->select_lex.select_n_having_items+=
        table->select_lex->select_n_having_items;

unknown's avatar
VIEW  
unknown committed
1207
      /*
1208 1209 1210 1211
        Tables of the main select of the view should be marked as belonging
        to the same select as original view (again we can use LEX::select_lex
        for this purprose because we don't support MERGE algorithm for views
        with unions).
unknown's avatar
VIEW  
unknown committed
1212
      */
1213
      for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local)
1214 1215
        tbl->select_lex= table->select_lex;

1216
      {
1217
        if (view_main_select_tables->next_local)
unknown's avatar
unknown committed
1218
        {
unknown's avatar
unknown committed
1219
          table->multitable_view= TRUE;
unknown's avatar
unknown committed
1220 1221 1222
          if (table->belong_to_view)
           table->belong_to_view->multitable_view= TRUE;
        }
1223 1224 1225 1226 1227 1228 1229 1230
        /* make nested join structure for view tables */
        NESTED_JOIN *nested_join;
        if (!(nested_join= table->nested_join=
              (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
          goto err;
        nested_join->join_list= view_select->top_join_list;

        /* re-nest tables of VIEW */
unknown's avatar
unknown committed
1231 1232
        ti.rewind();
        while ((tbl= ti++))
1233
        {
unknown's avatar
unknown committed
1234 1235
          tbl->join_list= &nested_join->join_list;
          tbl->embedding= table;
1236 1237
        }
      }
unknown's avatar
VIEW  
unknown committed
1238

1239
      /* Store WHERE clause for post-processing in setup_underlying */
1240
      table->where= view_select->where;
1241
      /*
1242 1243 1244 1245
        Add subqueries units to SELECT into which we merging current view.
        unit(->next)* chain starts with subqueries that are used by this
        view and continues with subqueries that are used by other views.
        We must not add any subquery twice (otherwise we'll form a loop),
1246
        to do this we remember in end_unit the first subquery that has
1247
        been already added.
1248

1249 1250
        NOTE: we do not support UNION here, so we take only one select
      */
1251
      SELECT_LEX_NODE *end_unit= table->select_lex->slave;
1252
      SELECT_LEX_UNIT *next_unit;
1253 1254
      for (SELECT_LEX_UNIT *unit= lex->select_lex.first_inner_unit();
           unit;
1255
           unit= next_unit)
1256
      {
1257 1258
        if (unit == end_unit)
          break;
1259 1260
        SELECT_LEX_NODE *save_slave= unit->slave;
        next_unit= unit->next_unit();
1261 1262 1263 1264
        unit->include_down(table->select_lex);
        unit->slave= save_slave; // fix include_down initialisation
      }

unknown's avatar
VIEW  
unknown committed
1265 1266 1267 1268 1269 1270 1271 1272 1273
      /*
	This SELECT_LEX will be linked in global SELECT_LEX list
	to make it processed by mysql_handle_derived(),
	but it will not be included to SELECT_LEX tree, because it
	will not be executed
      */
      goto ok;
    }

1274
    table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
1275
    DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
1276
    view_select->linkage= DERIVED_TABLE_TYPE;
1277
    table->updatable= 0;
1278
    table->effective_with_check= VIEW_CHECK_NONE;
unknown's avatar
unknown committed
1279
    old_lex->subqueries= TRUE;
unknown's avatar
VIEW  
unknown committed
1280 1281 1282

    /* SELECT tree link */
    lex->unit.include_down(table->select_lex);
1283
    lex->unit.slave= view_select; // fix include_down initialisation
unknown's avatar
VIEW  
unknown committed
1284 1285 1286 1287 1288 1289 1290 1291

    table->derived= &lex->unit;
  }
  else
    goto err;

ok:
  /* global SELECT list linking */
1292
  end= view_select;	// primary SELECT_LEX is always last
unknown's avatar
VIEW  
unknown committed
1293 1294 1295 1296 1297 1298
  end->link_next= old_lex->all_selects_list;
  old_lex->all_selects_list->link_prev= &end->link_next;
  old_lex->all_selects_list= lex->all_selects_list;
  lex->all_selects_list->link_prev=
    (st_select_lex_node**)&old_lex->all_selects_list;

1299
ok2:
1300 1301
  if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used)
    old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used;
1302 1303
  DBUG_ASSERT(lex == thd->lex);
  thd->lex= old_lex;                            // Needed for prepare_security
1304
  result= !table->prelocking_placeholder && table->prepare_security(thd);
unknown's avatar
VIEW  
unknown committed
1305

1306
  lex_end(lex);
1307
end:
unknown's avatar
VIEW  
unknown committed
1308
  if (arena)
unknown's avatar
Rename:  
unknown committed
1309
    thd->restore_active_arena(arena, &backup);
1310 1311 1312 1313
  thd->lex= old_lex;
  DBUG_RETURN(result);

err:
unknown's avatar
unknown committed
1314 1315
  DBUG_ASSERT(thd->lex == table->view);
  lex_end(thd->lex);
1316
  delete table->view;
unknown's avatar
VIEW  
unknown committed
1317
  table->view= 0;	// now it is not VIEW placeholder
1318 1319
  result= 1;
  goto end;
unknown's avatar
VIEW  
unknown committed
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
}


/*
  drop view

  SYNOPSIS
    mysql_drop_view()
    thd		- thread handler
    views	- views to delete
    drop_mode	- cascade/check

  RETURN VALUE
unknown's avatar
unknown committed
1333 1334
    FALSE OK
    TRUE  Error
unknown's avatar
VIEW  
unknown committed
1335
*/
1336

unknown's avatar
unknown committed
1337
bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
unknown's avatar
VIEW  
unknown committed
1338 1339 1340
{
  char path[FN_REFLEN];
  TABLE_LIST *view;
1341 1342 1343
  String non_existant_views;
  char *wrong_object_db= NULL, *wrong_object_name= NULL;
  bool error= FALSE;
unknown's avatar
unknown committed
1344
  enum legacy_db_type not_used;
unknown's avatar
unknown committed
1345
  DBUG_ENTER("mysql_drop_view");
unknown's avatar
VIEW  
unknown committed
1346

1347
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
VIEW  
unknown committed
1348 1349
  for (view= views; view; view= view->next_local)
  {
unknown's avatar
unknown committed
1350
    TABLE_SHARE *share;
unknown's avatar
unknown committed
1351
    frm_type_enum type= FRMTYPE_ERROR;
1352
    build_table_filename(path, sizeof(path),
1353
                         view->db, view->table_name, reg_ext, 0);
unknown's avatar
unknown committed
1354

1355 1356
    if (access(path, F_OK) || 
        FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
unknown's avatar
VIEW  
unknown committed
1357 1358
    {
      char name[FN_REFLEN];
1359
      my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
unknown's avatar
VIEW  
unknown committed
1360 1361 1362 1363 1364 1365 1366
      if (thd->lex->drop_if_exists)
      {
	push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
			    ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
			    name);
	continue;
      }
1367 1368 1369 1370 1371 1372 1373 1374
      if (type == FRMTYPE_TABLE)
      {
        if (!wrong_object_name)
        {
          wrong_object_db= view->db;
          wrong_object_name= view->table_name;
        }
      }
unknown's avatar
VIEW  
unknown committed
1375
      else
1376 1377 1378 1379 1380 1381
      {
        if (non_existant_views.length())
          non_existant_views.append(',');
        non_existant_views.append(String(view->table_name,system_charset_info));
      }
      continue;
unknown's avatar
VIEW  
unknown committed
1382 1383
    }
    if (my_delete(path, MYF(MY_WME)))
1384
      error= TRUE;
unknown's avatar
unknown committed
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398

    /*
      For a view, there is only one table_share object which should never
      be used outside of LOCK_open
    */
    if ((share= get_cached_table_share(view->db, view->table_name)))
    {
      DBUG_ASSERT(share->ref_count == 0);
      pthread_mutex_lock(&share->mutex);
      share->ref_count++;
      share->version= 0;
      pthread_mutex_unlock(&share->mutex);
      release_table_share(share, RELEASE_WAIT_FOR_DROP);
    }
1399
    query_cache_invalidate3(thd, view, 0);
unknown's avatar
unknown committed
1400
    sp_cache_invalidate();
unknown's avatar
VIEW  
unknown committed
1401
  }
1402

1403 1404
  if (error)
  {
unknown's avatar
unknown committed
1405
    VOID(pthread_mutex_unlock(&LOCK_open));
1406 1407 1408 1409
    DBUG_RETURN(TRUE);
  }
  if (wrong_object_name)
  {
unknown's avatar
unknown committed
1410
    VOID(pthread_mutex_unlock(&LOCK_open));
1411 1412 1413 1414 1415 1416
    my_error(ER_WRONG_OBJECT, MYF(0), wrong_object_db, wrong_object_name, 
             "VIEW");
    DBUG_RETURN(TRUE);
  }
  if (non_existant_views.length())
  {
unknown's avatar
unknown committed
1417
    VOID(pthread_mutex_unlock(&LOCK_open));
1418 1419 1420
    my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr());
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
1421 1422 1423 1424 1425 1426 1427 1428

  if (mysql_bin_log.is_open())
  {
    thd->clear_error();
    thd->binlog_query(THD::STMT_QUERY_TYPE,
                      thd->query, thd->query_length, FALSE, FALSE);
  }

unknown's avatar
VIEW  
unknown committed
1429
  send_ok(thd);
unknown's avatar
unknown committed
1430
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1431
  DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
}


/*
  Check type of .frm if we are not going to parse it

  SYNOPSIS
    mysql_frm_type()
    path	path to file

  RETURN
    FRMTYPE_ERROR	error
    FRMTYPE_TABLE	table
    FRMTYPE_VIEW	view
*/

unknown's avatar
unknown committed
1448
frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
unknown's avatar
VIEW  
unknown committed
1449 1450
{
  File file;
1451 1452
  uchar header[10];	//"TYPE=VIEW\n" it is 10 characters
  int error;
unknown's avatar
VIEW  
unknown committed
1453 1454
  DBUG_ENTER("mysql_frm_type");

1455 1456
  *dbt= DB_TYPE_UNKNOWN;

1457
  if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
unknown's avatar
VIEW  
unknown committed
1458
    DBUG_RETURN(FRMTYPE_ERROR);
1459
  error= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME | MY_NABP));
unknown's avatar
VIEW  
unknown committed
1460
  my_close(file, MYF(MY_WME));
1461 1462

  if (error)
1463
    DBUG_RETURN(FRMTYPE_ERROR);
1464
  if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
1465
    DBUG_RETURN(FRMTYPE_VIEW);
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476

  /*
    This is just a check for DB_TYPE. We'll return default unknown type
    if the following test is true (arg #3). This should not have effect
    on return value from this function (default FRMTYPE_TABLE)
  */
  if (header[0] != (uchar) 254 || header[1] != 1 ||
      (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
       (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
    DBUG_RETURN(FRMTYPE_TABLE);

unknown's avatar
unknown committed
1477
  *dbt= (enum legacy_db_type) (uint) *(header + 3);
1478
  DBUG_RETURN(FRMTYPE_TABLE);                   // Is probably a .frm table
unknown's avatar
VIEW  
unknown committed
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
}


/*
  check of key (primary or unique) presence in updatable view

  SYNOPSIS
    check_key_in_view()
    thd     thread handler
    view    view for check with opened table

1490
  DESCRIPTION
1491 1492
    If it is VIEW and query have LIMIT clause then check that underlying
    table of view contain one of following:
1493 1494 1495 1496 1497
      1) primary key of underlying table
      2) unique key underlying table with fields for which NULL value is
         impossible
      3) all fields of underlying table

unknown's avatar
VIEW  
unknown committed
1498 1499 1500 1501 1502 1503 1504
  RETURN
    FALSE   OK
    TRUE    view do not contain key or all fields
*/

bool check_key_in_view(THD *thd, TABLE_LIST *view)
{
1505
  TABLE *table;
1506
  Field_translator *trans, *end_of_trans;
1507
  KEY *key_info, *key_info_end;
unknown's avatar
VIEW  
unknown committed
1508
  DBUG_ENTER("check_key_in_view");
1509

1510
  /*
unknown's avatar
merge  
unknown committed
1511
    we do not support updatable UNIONs in VIEW, so we can check just limit of
1512 1513
    LEX::select_lex
  */
1514 1515 1516
  if ((!view->view && !view->belong_to_view) ||
      thd->lex->sql_command == SQLCOM_INSERT ||
      thd->lex->select_lex.select_limit == 0)
1517
    DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
1518
  table= view->table;
1519
  view= view->top_table();
1520
  trans= view->field_translation;
1521
  key_info_end= (key_info= table->key_info)+ table->s->keys;
unknown's avatar
VIEW  
unknown committed
1522

1523
  end_of_trans=  view->field_translation_end;
1524
  DBUG_ASSERT(table != 0 && view->field_translation != 0);
unknown's avatar
VIEW  
unknown committed
1525

1526 1527 1528 1529 1530 1531
  {
    /*
      We should be sure that all fields are ready to get keys from them, but
      this operation should not have influence on Field::query_id, to avoid
      marking as used fields which are not used
    */
1532 1533 1534
    enum_mark_columns save_mark_used_columns= thd->mark_used_columns;
    thd->mark_used_columns= MARK_COLUMNS_NONE;
    DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
1535 1536 1537
    for (Field_translator *fld= trans; fld < end_of_trans; fld++)
    {
      if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item))
unknown's avatar
unknown committed
1538
      {
1539
        thd->mark_used_columns= save_mark_used_columns;
1540
        return TRUE;
unknown's avatar
unknown committed
1541
      }
1542
    }
1543 1544
    thd->mark_used_columns= save_mark_used_columns;
    DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
1545
  }
1546 1547
  /* Loop over all keys to see if a unique-not-null key is used */
  for (;key_info != key_info_end ; key_info++)
unknown's avatar
VIEW  
unknown committed
1548
  {
1549
    if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
unknown's avatar
VIEW  
unknown committed
1550 1551
    {
      KEY_PART_INFO *key_part= key_info->key_part;
1552 1553 1554 1555
      KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;

      /* check that all key parts are used */
      for (;;)
unknown's avatar
VIEW  
unknown committed
1556
      {
1557 1558
        Field_translator *k;
        for (k= trans; k < end_of_trans; k++)
unknown's avatar
VIEW  
unknown committed
1559
        {
1560
          Item_field *field;
1561
          if ((field= k->item->filed_for_view_update()) &&
1562
              field->field == key_part->field)
unknown's avatar
VIEW  
unknown committed
1563 1564
            break;
        }
1565
        if (k == end_of_trans)
1566 1567 1568
          break;                                // Key is not possible
        if (++key_part == key_part_end)
          DBUG_RETURN(FALSE);                   // Found usable key
unknown's avatar
VIEW  
unknown committed
1569 1570 1571 1572
      }
    }
  }

1573
  DBUG_PRINT("info", ("checking if all fields of table are used"));
unknown's avatar
VIEW  
unknown committed
1574 1575
  /* check all fields presence */
  {
1576
    Field **field_ptr;
1577
    Field_translator *fld;
1578
    for (field_ptr= table->field; *field_ptr; field_ptr++)
unknown's avatar
VIEW  
unknown committed
1579
    {
1580
      for (fld= trans; fld < end_of_trans; fld++)
unknown's avatar
VIEW  
unknown committed
1581
      {
1582
        Item_field *field;
1583
        if ((field= fld->item->filed_for_view_update()) &&
1584
            field->field == *field_ptr)
unknown's avatar
VIEW  
unknown committed
1585 1586
          break;
      }
1587
      if (fld == end_of_trans)                // If field didn't exists
unknown's avatar
VIEW  
unknown committed
1588
      {
1589
        /*
1590
          Keys or all fields of underlying tables are not found => we have
1591 1592
          to check variable updatable_views_with_limit to decide should we
          issue an error or just a warning
1593
        */
1594
        if (thd->variables.updatable_views_with_limit)
unknown's avatar
VIEW  
unknown committed
1595
        {
1596 1597 1598 1599
          /* update allowed, but issue warning */
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                       ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY));
          DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
1600
        }
1601 1602
        /* prohibit update */
        DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614
      }
    }
  }
  DBUG_RETURN(FALSE);
}


/*
  insert fields from VIEW (MERGE algorithm) into given list

  SYNOPSIS
    insert_view_fields()
1615
    thd       thread handler
unknown's avatar
VIEW  
unknown committed
1616 1617
    list      list for insertion
    view      view for processing
1618 1619

  RETURN
unknown's avatar
unknown committed
1620 1621
    FALSE OK
    TRUE  error (is not sent to cliet)
unknown's avatar
VIEW  
unknown committed
1622 1623
*/

1624
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
unknown's avatar
VIEW  
unknown committed
1625
{
1626
  Field_translator *trans_end;
1627
  Field_translator *trans;
unknown's avatar
VIEW  
unknown committed
1628
  DBUG_ENTER("insert_view_fields");
1629 1630

  if (!(trans= view->field_translation))
unknown's avatar
unknown committed
1631
    DBUG_RETURN(FALSE);
1632
  trans_end= view->field_translation_end;
unknown's avatar
VIEW  
unknown committed
1633

1634
  for (Field_translator *entry= trans; entry < trans_end; entry++)
unknown's avatar
VIEW  
unknown committed
1635
  {
1636
    Item_field *fld;
1637
    if ((fld= entry->item->filed_for_view_update()))
1638
      list->push_back(fld);
1639 1640
    else
    {
1641
      my_error(ER_NON_INSERTABLE_TABLE, MYF(0), view->alias, "INSERT");
unknown's avatar
unknown committed
1642
      DBUG_RETURN(TRUE);
1643
    }
unknown's avatar
VIEW  
unknown committed
1644
  }
unknown's avatar
unknown committed
1645
  DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
1646
}
1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671

/*
  checking view md5 check suum

  SINOPSYS
    view_checksum()
    thd     threar handler
    view    view for check

  RETUIRN
    HA_ADMIN_OK               OK
    HA_ADMIN_NOT_IMPLEMENTED  it is not VIEW
    HA_ADMIN_WRONG_CHECKSUM   check sum is wrong
*/

int view_checksum(THD *thd, TABLE_LIST *view)
{
  char md5[MD5_BUFF_LENGTH];
  if (!view->view || view->md5.length != 32)
    return HA_ADMIN_NOT_IMPLEMENTED;
  view->calc_md5(md5);
  return (strncmp(md5, view->md5.str, 32) ?
          HA_ADMIN_WRONG_CHECKSUM :
          HA_ADMIN_OK);
}
1672

1673 1674
/*
  rename view
1675

1676 1677
  Synopsis:
    renames a view
1678

1679 1680 1681 1682
  Parameters:
    thd        thread handler
    new_name   new name of view
    view       view
1683

1684 1685 1686 1687
  Return values:
    FALSE      Ok 
    TRUE       Error
*/
1688 1689
bool
mysql_rename_view(THD *thd,
1690 1691
                  const char *new_name,
                  TABLE_LIST *view)
1692
{
1693
  LEX_STRING pathstr;
1694
  File_parser *parser;
1695
  char path_buff[FN_REFLEN];
1696
  bool error= TRUE;
1697 1698
  DBUG_ENTER("mysql_rename_view");

1699 1700 1701 1702
  pathstr.str= (char *) path_buff;
  pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
                                       view->db, view->table_name,
                                       reg_ext, 0);
1703 1704

  if ((parser= sql_parse_prepare(&pathstr, thd->mem_root, 1)) && 
1705 1706
       is_equal(&view_type, parser->type()))
  {
1707
    TABLE_LIST view_def;
1708 1709
    char dir_buff[FN_REFLEN];
    LEX_STRING dir, file;
1710

1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
    /*
      To be PS-friendly we should either to restore state of
      TABLE_LIST object pointed by 'view' after using it for
      view definition parsing or use temporary 'view_def'
      object for it.
    */
    bzero(&view_def, sizeof(view_def));
    view_def.timestamp.str= view_def.timestamp_buffer;
    view_def.view_suid= TRUE;

1721
    /* get view definition and source */
1722
    if (parser->parse((gptr)&view_def, thd->mem_root, view_parameters,
1723 1724
                      array_elements(view_parameters)-1,
                      &file_parser_dummy_hook))
1725
      goto err;
1726 1727

    /* rename view and it's backups */
1728
    if (rename_in_schema_file(view->db, view->table_name, new_name, 
1729
                              view_def.revision - 1, num_view_backups))
1730
      goto err;
1731

1732 1733 1734
    dir.str= dir_buff;
    dir.length= build_table_filename(dir_buff, sizeof(dir_buff) - 1,
                                     view->db, "", "", 0);
1735

1736 1737 1738
    pathstr.str= path_buff;
    pathstr.length= build_table_filename(path_buff, sizeof(path_buff) - 1,
                                      view->db, new_name, reg_ext, 0);
1739

1740 1741
    file.str= pathstr.str + dir.length;
    file.length= pathstr.length - dir.length;
1742

1743
    if (sql_create_definition_file(&dir, &file, view_file_type,
1744
                                   (gptr)&view_def, view_parameters,
1745 1746
                                   num_view_backups)) 
    {
1747
      /* restore renamed view in case of error */
1748
      rename_in_schema_file(view->db, new_name, view->table_name, 
1749
                            view_def.revision - 1, num_view_backups);
1750
      goto err;
1751 1752 1753 1754 1755 1756 1757
    }
  } else
    DBUG_RETURN(1);  

  /* remove cache entries */
  query_cache_invalidate3(thd, view, 0);
  sp_cache_invalidate();
1758 1759 1760 1761
  error= FALSE;

err:
  DBUG_RETURN(error);
1762
}