sql_view.cc 38 KB
Newer Older
unknown's avatar
VIEW  
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* 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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

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

#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

27 28
const LEX_STRING view_type= { (char*) STRING_WITH_LEN("VIEW") };

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
}


unknown's avatar
VIEW  
unknown committed
158 159 160 161 162 163 164 165 166
/*
  Creating/altering VIEW procedure

  SYNOPSIS
    mysql_create_view()
    thd		- thread handler
    mode	- VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE

  RETURN VALUE
unknown's avatar
unknown committed
167 168
     FALSE OK
     TRUE  Error
unknown's avatar
VIEW  
unknown committed
169
*/
170

unknown's avatar
unknown committed
171 172
bool mysql_create_view(THD *thd,
                       enum_view_create_mode mode)
unknown's avatar
VIEW  
unknown committed
173 174 175 176 177 178
{
  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
179
  TABLE_LIST *tbl;
unknown's avatar
unknown committed
180 181 182 183
  SELECT_LEX *select_lex= &lex->select_lex;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  SELECT_LEX *sl;
#endif
unknown's avatar
VIEW  
unknown committed
184
  SELECT_LEX_UNIT *unit= &lex->unit;
unknown's avatar
unknown committed
185
  bool res= FALSE;
unknown's avatar
VIEW  
unknown committed
186 187
  DBUG_ENTER("mysql_create_view");

188 189 190 191 192 193
  if (lex->proc_list.first ||
      lex->result)
  {
    my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), (lex->result ?
                                             "INTO" :
                                             "PROCEDURE"));
unknown's avatar
unknown committed
194
    res= TRUE;
195 196 197
    goto err;
  }
  if (lex->derived_tables ||
unknown's avatar
VIEW  
unknown committed
198 199
      lex->variables_used || lex->param_list.elements)
  {
unknown's avatar
unknown committed
200
    int err= (lex->derived_tables ?
201
              ER_VIEW_SELECT_DERIVED :
unknown's avatar
unknown committed
202 203
              ER_VIEW_SELECT_VARIABLE);
    my_message(err, ER(err), MYF(0));
unknown's avatar
unknown committed
204
    res= TRUE;
unknown's avatar
VIEW  
unknown committed
205 206 207
    goto err;
  }

unknown's avatar
unknown committed
208 209 210
  if (mode != VIEW_CREATE_NEW)
    sp_cache_invalidate();

unknown's avatar
VIEW  
unknown committed
211
#ifndef NO_EMBEDDED_ACCESS_CHECKS
212 213
  /*
    Privilege check for view creation:
unknown's avatar
unknown committed
214 215
    - user has CREATE VIEW privilege on view table
    - user has DROP privilege in case of ALTER VIEW or CREATE OR REPLACE
216
    VIEW
unknown's avatar
unknown committed
217
    - user has some (SELECT/UPDATE/INSERT/DELETE) privileges on columns of
218 219 220
    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
221
    - user has SELECT privilege on columns used in expressions of VIEW select
222 223 224 225
    - 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)
  */
226 227 228 229
  if ((check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
                    0, 0) ||
       grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) ||
      (mode != VIEW_CREATE_NEW &&
230
       (check_access(thd, DROP_ACL, view->db, &view->grant.privilege,
231
                     0, 0) ||
232
        grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0))))
unknown's avatar
unknown committed
233 234 235 236
  {
    res= TRUE;
    goto err;
  }
237
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
238
  {
239
    for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
unknown's avatar
VIEW  
unknown committed
240
    {
241
      /*
242
        Ensure that we have some privileges on this table, more strict check
243 244
        will be done on column level after preparation,
      */
unknown's avatar
unknown committed
245
      if (check_some_access(thd, VIEW_ANY_ACL, tbl))
246
      {
247
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
248
                 "ANY", thd->priv_user, thd->host_or_ip, tbl->table_name);
unknown's avatar
unknown committed
249 250
        res= TRUE;
        goto err;
251
      }
252 253 254 255
      /*
        Mark this table as a table which will be checked after the prepare
        phase
      */
256
      tbl->table_in_first_from_clause= 1;
unknown's avatar
VIEW  
unknown committed
257

258
      /*
259 260 261
        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
262 263 264
      */
      tbl->grant.want_privilege= SELECT_ACL;
      /*
265
        Make sure that all rights are loaded to the TABLE::grant field.
266

267
        tbl->table_name will be correct name of table because VIEWs are
268 269 270
        not opened yet.
      */
      fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
271
                                      tbl->table_name);
272
    }
unknown's avatar
VIEW  
unknown committed
273 274 275 276 277
  }

  if (&lex->select_lex != lex->all_selects_list)
  {
    /* check tables of subqueries */
unknown's avatar
unknown committed
278
    for (tbl= tables; tbl; tbl= tbl->next_global)
unknown's avatar
VIEW  
unknown committed
279 280 281 282 283 284 285
    {
      if (!tbl->table_in_first_from_clause)
      {
        if (check_access(thd, SELECT_ACL, tbl->db,
                         &tbl->grant.privilege, 0, 0) ||
            grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
        {
unknown's avatar
unknown committed
286
          res= TRUE;
unknown's avatar
VIEW  
unknown committed
287 288 289 290 291 292
          goto err;
        }
      }
    }
  }
  /*
293
    Mark fields for special privilege check ("any" privilege)
unknown's avatar
VIEW  
unknown committed
294
  */
295
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
296
  {
297
    List_iterator_fast<Item> it(sl->item_list);
unknown's avatar
VIEW  
unknown committed
298 299 300
    Item *item;
    while ((item= it++))
    {
301 302 303
      Item_field *field;
      if ((field= item->filed_for_view_update()))
        field->any_privileges= 1;
unknown's avatar
VIEW  
unknown committed
304 305 306 307
    }
  }
#endif

unknown's avatar
unknown committed
308
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
309 310 311 312
  {
    res= TRUE;
    goto err;
  }
unknown's avatar
VIEW  
unknown committed
313

314 315
  /*
    check that tables are not temporary  and this VIEW do not used in query
unknown's avatar
unknown committed
316 317 318 319
    (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. 
  */ 
320
  for (tbl= lex->query_tables; tbl; tbl= tbl->next_global)
unknown's avatar
VIEW  
unknown committed
321
  {
322
    /* is this table temporary and is not view? */
323
    if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view &&
324
        !tbl->schema_table)
unknown's avatar
VIEW  
unknown committed
325 326
    {
      my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
unknown's avatar
unknown committed
327
      res= TRUE;
unknown's avatar
VIEW  
unknown committed
328 329 330
      goto err;
    }

331 332 333
    /* is this table view and the same view which we creates now? */
    if (tbl->view &&
        strcmp(tbl->view_db.str, view->db) == 0 &&
334
        strcmp(tbl->view_name.str, view->table_name) == 0)
335 336
    {
      my_error(ER_NO_SUCH_TABLE, MYF(0), tbl->view_db.str, tbl->view_name.str);
unknown's avatar
unknown committed
337
      res= TRUE;
338 339 340
      goto err;
    }

unknown's avatar
VIEW  
unknown committed
341
    /*
342
      Copy the privileges of the underlying VIEWs which were filled by
unknown's avatar
VIEW  
unknown committed
343
      fill_effective_table_privileges
344
      (they were not copied at derived tables processing)
unknown's avatar
VIEW  
unknown committed
345 346 347 348
    */
    tbl->table->grant.privilege= tbl->grant.privilege;
  }

349
  /* prepare select to resolve all fields */
unknown's avatar
VIEW  
unknown committed
350
  lex->view_prepare_mode= 1;
unknown's avatar
unknown committed
351
  if (unit->prepare(thd, 0, 0, view->view_name.str))
352 353 354 355 356
  {
    /*
      some errors from prepare are reported to user, if is not then
      it will be checked after err: label
    */
unknown's avatar
unknown committed
357
    res= TRUE;
unknown's avatar
VIEW  
unknown committed
358
    goto err;
359
  }
unknown's avatar
VIEW  
unknown committed
360 361 362 363 364 365 366 367

  /* 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;
368 369

    if (lex->view_list.elements != select_lex->item_list.elements)
unknown's avatar
VIEW  
unknown committed
370
    {
371 372
      my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0));
      goto err;
unknown's avatar
VIEW  
unknown committed
373
    }
374
    while ((item= it++, name= nm++))
375
    {
376
      item->set_name(name->str, name->length, system_charset_info);
377 378
      item->is_autogenerated_name= FALSE;
    }
unknown's avatar
VIEW  
unknown committed
379 380
  }

381
  if (check_duplicate_names(select_lex->item_list, 1))
unknown's avatar
unknown committed
382 383 384 385
  {
    res= TRUE;
    goto err;
  }
386

unknown's avatar
VIEW  
unknown committed
387 388
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  /*
389
    Compare/check grants on view with grants of underlying tables
unknown's avatar
VIEW  
unknown committed
390
  */
391
  for (sl= select_lex; sl; sl= sl->next_select())
unknown's avatar
VIEW  
unknown committed
392 393
  {
    char *db= view->db ? view->db : thd->db;
394
    List_iterator_fast<Item> it(sl->item_list);
unknown's avatar
VIEW  
unknown committed
395 396
    Item *item;
    fill_effective_table_privileges(thd, &view->grant, db,
397
                                    view->table_name);
398
    while ((item= it++))
unknown's avatar
VIEW  
unknown committed
399
    {
400
      Item_field *fld;
unknown's avatar
VIEW  
unknown committed
401
      uint priv= (get_column_grant(thd, &view->grant, db,
402
                                    view->table_name, item->name) &
unknown's avatar
VIEW  
unknown committed
403
                  VIEW_ANY_ACL);
404
      if ((fld= item->filed_for_view_update()))
unknown's avatar
VIEW  
unknown committed
405 406
      {
        /*
407
          Do we have more privileges on view field then underlying table field?
unknown's avatar
VIEW  
unknown committed
408
        */
409
        if (!fld->field->table->s->tmp_table && (~fld->have_privileges & priv))
unknown's avatar
VIEW  
unknown committed
410 411
        {
          /* VIEW column has more privileges */
412 413
          my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
                   "create view", thd->priv_user, thd->host_or_ip, item->name,
414
                   view->table_name);
unknown's avatar
unknown committed
415 416
          res= TRUE;
          goto err;
unknown's avatar
VIEW  
unknown committed
417 418 419 420 421 422
        }
      }
    }
  }
#endif

423
  if (wait_if_global_read_lock(thd, 0, 0))
unknown's avatar
VIEW  
unknown committed
424
  {
unknown's avatar
unknown committed
425
    res= TRUE;
unknown's avatar
VIEW  
unknown committed
426 427 428
    goto err;
  }
  VOID(pthread_mutex_lock(&LOCK_open));
429
  res= mysql_register_view(thd, view, mode);
unknown's avatar
VIEW  
unknown committed
430
  VOID(pthread_mutex_unlock(&LOCK_open));
431 432
  if (view->revision != 1)
    query_cache_invalidate3(thd, view, 0);
unknown's avatar
VIEW  
unknown committed
433
  start_waiting_global_read_lock(thd);
434 435
  if (res)
    goto err;
unknown's avatar
VIEW  
unknown committed
436 437 438

  send_ok(thd);
  lex->link_first_table_back(view, link_to_local);
unknown's avatar
unknown committed
439
  DBUG_RETURN(0);
unknown's avatar
VIEW  
unknown committed
440 441 442 443 444

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


449 450 451
/* index of revision number in following table */
static const int revision_number_position= 5;
/* index of last required parameter for making view */
unknown's avatar
unknown committed
452
static const int required_view_parameters= 7;
unknown's avatar
VIEW  
unknown committed
453

454 455 456 457 458 459
/*
  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
460
static File_option view_parameters[]=
461
{{{(char*) "query", 5},		offsetof(TABLE_LIST, query),
unknown's avatar
VIEW  
unknown committed
462
  FILE_OPTIONS_STRING},
463
 {{(char*) "md5", 3},		offsetof(TABLE_LIST, md5),
unknown's avatar
VIEW  
unknown committed
464
  FILE_OPTIONS_STRING},
unknown's avatar
unknown committed
465
 {{(char*) "updatable", 9},	offsetof(TABLE_LIST, updatable_view),
unknown's avatar
VIEW  
unknown committed
466
  FILE_OPTIONS_ULONGLONG},
467
 {{(char*) "algorithm", 9},	offsetof(TABLE_LIST, algorithm),
unknown's avatar
VIEW  
unknown committed
468
  FILE_OPTIONS_ULONGLONG},
unknown's avatar
unknown committed
469
 {{(char*) "with_check_option", 17}, offsetof(TABLE_LIST, with_check),
unknown's avatar
unknown committed
470
   FILE_OPTIONS_ULONGLONG},
471
 {{(char*) "revision", 8},	offsetof(TABLE_LIST, revision),
unknown's avatar
VIEW  
unknown committed
472
  FILE_OPTIONS_REV},
473
 {{(char*) "timestamp", 9},	offsetof(TABLE_LIST, timestamp),
unknown's avatar
VIEW  
unknown committed
474
  FILE_OPTIONS_TIMESTAMP},
475
 {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version),
unknown's avatar
VIEW  
unknown committed
476
  FILE_OPTIONS_ULONGLONG},
477
 {{(char*) "source", 6},	offsetof(TABLE_LIST, source),
unknown's avatar
VIEW  
unknown committed
478
  FILE_OPTIONS_ESTRING},
unknown's avatar
unknown committed
479
 {{NullS, 0},			0,
unknown's avatar
VIEW  
unknown committed
480 481 482
  FILE_OPTIONS_STRING}
};

483
static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }};
unknown's avatar
VIEW  
unknown committed
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499


/*
  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
*/
500

unknown's avatar
VIEW  
unknown committed
501 502 503
static int mysql_register_view(THD *thd, TABLE_LIST *view,
			       enum_view_create_mode mode)
{
504
  LEX *lex= thd->lex;
unknown's avatar
VIEW  
unknown committed
505 506
  char buff[4096];
  String str(buff,(uint32) sizeof(buff), system_charset_info);
507
  char md5[MD5_BUFF_LENGTH];
unknown's avatar
VIEW  
unknown committed
508 509 510 511 512
  bool can_be_merged;
  char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
  LEX_STRING dir, file;
  DBUG_ENTER("mysql_register_view");

513
  /* print query */
unknown's avatar
VIEW  
unknown committed
514
  str.length(0);
515 516 517
  {
    ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
    thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
518
    lex->unit.print(&str);
519 520
    thd->variables.sql_mode|= sql_mode;
  }
unknown's avatar
VIEW  
unknown committed
521
  str.append('\0');
522
  DBUG_PRINT("info", ("View: %s", str.ptr()));
unknown's avatar
VIEW  
unknown committed
523

524
  /* print file name */
unknown's avatar
VIEW  
unknown committed
525 526 527 528 529 530 531
  (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/",
		     mysql_data_home, view->db);
  unpack_filename(dir_buff, dir_buff);
  dir.str= dir_buff;
  dir.length= strlen(dir_buff);

  file.str= file_buff;
532
  file.length= (strxnmov(file_buff, FN_REFLEN, view->table_name, reg_ext,
533
                         NullS) - file_buff);
unknown's avatar
VIEW  
unknown committed
534
  /* init timestamp */
535
  if (!view->timestamp.str)
unknown's avatar
VIEW  
unknown committed
536 537
    view->timestamp.str= view->timestamp_buffer;

538
  /* check old .frm */
unknown's avatar
VIEW  
unknown committed
539 540 541
  {
    char path_buff[FN_REFLEN];
    LEX_STRING path;
542
    File_parser *parser;
unknown's avatar
unknown committed
543

unknown's avatar
VIEW  
unknown committed
544 545 546 547 548 549 550 551 552
    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
553
	DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
554 555
      }

unknown's avatar
unknown committed
556
      if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0)))
557 558
	DBUG_RETURN(1);

559
      if (!parser->ok() || !is_equal(&view_type, parser->type()))
unknown's avatar
VIEW  
unknown committed
560
      {
561
        my_error(ER_WRONG_OBJECT, MYF(0),
562
                 (view->db ? view->db : thd->db), view->table_name, "VIEW");
unknown's avatar
merge  
unknown committed
563
        DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
564
      }
565 566 567

      /*
        read revision number
568

569
        TODO: read dependence list, too, to process cascade/restrict
570 571
        TODO: special cascade/restrict procedure for alter?
      */
unknown's avatar
unknown committed
572
      if (parser->parse((gptr)view, thd->mem_root,
573
                        view_parameters + revision_number_position, 1))
unknown's avatar
VIEW  
unknown committed
574
      {
unknown's avatar
merge  
unknown committed
575
        DBUG_RETURN(thd->net.report_error? -1 : 0);
unknown's avatar
VIEW  
unknown committed
576 577 578 579 580 581 582
      }
    }
    else
    {
      if (mode == VIEW_ALTER)
      {
	my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias);
unknown's avatar
unknown committed
583
	DBUG_RETURN(-1);
unknown's avatar
VIEW  
unknown committed
584 585 586
      }
    }
  }
587
  /* fill structure */
unknown's avatar
VIEW  
unknown committed
588 589 590 591 592 593 594 595
  view->query.str= (char*)str.ptr();
  view->query.length= str.length()-1; // we do not need last \0
  view->source.str= thd->query;
  view->source.length= thd->query_length;
  view->file_version= 1;
  view->calc_md5(md5);
  view->md5.str= md5;
  view->md5.length= 32;
596 597 598
  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
599 600 601
  {
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
                 ER(ER_WARN_VIEW_MERGE));
602
    lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
unknown's avatar
VIEW  
unknown committed
603
  }
604 605
  view->algorithm= lex->create_view_algorithm;
  view->with_check= lex->create_view_check;
606
  if ((view->updatable_view= (can_be_merged &&
unknown's avatar
unknown committed
607
                              view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
unknown's avatar
VIEW  
unknown committed
608
  {
609
    /* TODO: change here when we will support UNIONs */
610
    for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first;
611 612
	 tbl;
	 tbl= tbl->next_local)
unknown's avatar
VIEW  
unknown committed
613
    {
614
      if ((tbl->view && !tbl->updatable_view) || tbl->schema_table)
unknown's avatar
unknown committed
615
      {
616 617 618 619
	view->updatable_view= 0;
	break;
      }
      for (TABLE_LIST *up= tbl; up; up= up->embedding)
unknown's avatar
VIEW  
unknown committed
620
      {
621 622 623 624 625
	if (up->outer_join)
	{
	  view->updatable_view= 0;
	  goto loop_out;
	}
unknown's avatar
VIEW  
unknown committed
626 627 628
      }
    }
  }
629
loop_out:
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
  /*
    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,
645
				lex->query_tables->table_name))
646 647 648 649
  {
    view->updatable_view= 0;
  }

unknown's avatar
unknown committed
650 651 652
  if (view->with_check != VIEW_CHECK_NONE &&
      !view->updatable_view)
  {
653
    my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name);
unknown's avatar
unknown committed
654 655 656
    DBUG_RETURN(-1);
  }

unknown's avatar
VIEW  
unknown committed
657 658 659
  if (sql_create_definition_file(&dir, &file, view_file_type,
				 (gptr)view, view_parameters, 3))
  {
unknown's avatar
unknown committed
660
    DBUG_RETURN(thd->net.report_error? -1 : 1);
unknown's avatar
VIEW  
unknown committed
661 662 663 664 665 666 667 668 669 670 671 672
  }
  DBUG_RETURN(0);
}


/*
  read VIEW .frm and create structures

  SYNOPSIS
    mysql_make_view()
    parser		- parser object;
    table		- TABLE_LIST structure for filling
673 674

  RETURN
675 676
    0 ok
    1 error
unknown's avatar
VIEW  
unknown committed
677
*/
678

unknown's avatar
VIEW  
unknown committed
679 680 681 682
my_bool
mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
  DBUG_ENTER("mysql_make_view");
683
  DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name));
unknown's avatar
VIEW  
unknown committed
684 685 686 687

  if (table->view)
  {
    DBUG_PRINT("info",
688
               ("VIEW %s.%s is already processed on previous PS/SP execution",
unknown's avatar
VIEW  
unknown committed
689 690 691 692
                table->view_db.str, table->view_name.str));
    DBUG_RETURN(0);
  }

unknown's avatar
unknown committed
693
  SELECT_LEX *end;
unknown's avatar
VIEW  
unknown committed
694 695
  THD *thd= current_thd;
  LEX *old_lex= thd->lex, *lex;
696
  SELECT_LEX *view_select;
unknown's avatar
VIEW  
unknown committed
697 698 699 700 701 702
  int res= 0;

  /*
    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).
  */
unknown's avatar
unknown committed
703
  Query_arena *arena= thd->current_arena, backup;
unknown's avatar
unknown committed
704
  if (arena->is_conventional())
705 706
    arena= 0;
  else
unknown's avatar
VIEW  
unknown committed
707 708 709
    thd->set_n_backup_item_arena(arena, &backup);

  /* init timestamp */
710
  if (!table->timestamp.str)
unknown's avatar
VIEW  
unknown committed
711 712 713 714 715
    table->timestamp.str= table->timestamp_buffer;
  /*
    TODO: when VIEWs will be stored in cache, table mem_root should
    be used here
  */
unknown's avatar
unknown committed
716
  if (parser->parse((gptr)table, thd->mem_root, view_parameters,
unknown's avatar
unknown committed
717
                    required_view_parameters))
unknown's avatar
VIEW  
unknown committed
718 719 720 721 722 723 724 725
    goto err;

  /*
    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;
726 727
  table->view_name.str= table->table_name;
  table->view_name.length= table->table_name_length;
unknown's avatar
VIEW  
unknown committed
728

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

unknown's avatar
unknown committed
731 732 733 734 735 736
  /*
    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
737
  table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
unknown's avatar
unknown committed
738
  lex_start(thd, (uchar*)table->query.str, table->query.length);
739
  view_select= &lex->select_lex;
740
  view_select->select_number= ++thd->select_number;
unknown's avatar
VIEW  
unknown committed
741
  {
742
    ulong save_mode= thd->variables.sql_mode;
unknown's avatar
VIEW  
unknown committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
    /* 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
    */
767 768
    thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
                                MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
769
    CHARSET_INFO *save_cs= thd->variables.character_set_client;
unknown's avatar
unknown committed
770
    thd->variables.character_set_client= system_charset_info;
unknown's avatar
VIEW  
unknown committed
771
    res= yyparse((void *)thd);
772
    thd->variables.character_set_client= save_cs;
773
    thd->variables.sql_mode= save_mode;
unknown's avatar
VIEW  
unknown committed
774 775 776
  }
  if (!res && !thd->is_fatal_error)
  {
777 778 779
    TABLE_LIST *top_view= (table->belong_to_view ?
                           table->belong_to_view :
                           table);
780 781
    TABLE_LIST *view_tables= lex->query_tables;
    TABLE_LIST *view_tables_tail= 0;
782
    TABLE_LIST *tbl;
unknown's avatar
unknown committed
783

unknown's avatar
VIEW  
unknown committed
784
    /*
785 786 787
      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
788
    */
789 790
    if (!table->prelocking_placeholder &&
        (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
unknown's avatar
VIEW  
unknown committed
791
    {
792
      if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
793
          check_table_access(thd, SHOW_VIEW_ACL, table, 1))
unknown's avatar
VIEW  
unknown committed
794
      {
unknown's avatar
unknown committed
795
        my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
unknown's avatar
VIEW  
unknown committed
796 797 798
        goto err;
      }
    }
799 800
    else if (!table->prelocking_placeholder &&
             old_lex->sql_command == SQLCOM_SHOW_CREATE)
801 802 803 804
    {
      if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
        goto err;
    }
unknown's avatar
VIEW  
unknown committed
805

806 807 808 809 810 811 812 813 814 815 816 817
    /*
      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;
    }

818 819 820 821 822 823 824 825 826 827 828
    /*
      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)
      {
829
        view_tables_tail->next_global= table->next_global;
830 831 832 833
        table->next_global->prev_global= &view_tables_tail->next_global;
      }
      else
      {
834
        old_lex->query_tables_last= &view_tables_tail->next_global;
835 836 837 838 839
      }
      view_tables->prev_global= &table->next_global;
      table->next_global= view_tables;
    }

840 841 842 843 844 845 846 847 848 849
    /*
      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.
    */
    if (!lex->select_lex.next_select())
      for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local)
        tbl->lock_type= table->lock_type;

850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
    /*
      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;

    old_lex->derived_tables|= DERIVED_VIEW;

    /* 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;

unknown's avatar
VIEW  
unknown committed
867 868 869
    /*
      check MERGE algorithm ability
      - algorithm is not explicit TEMPORARY TABLE
870
      - VIEW SELECT allow merging
unknown's avatar
VIEW  
unknown committed
871 872
      - VIEW used in subquery or command support MERGE algorithm
    */
873
    if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
unknown's avatar
VIEW  
unknown committed
874 875
	lex->can_be_merged() &&
        (table->select_lex->master_unit() != &old_lex->unit ||
876 877
         old_lex->can_use_merged()) &&
        !old_lex->can_not_use_merged())
unknown's avatar
VIEW  
unknown committed
878
    {
unknown's avatar
unknown committed
879
      List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
unknown's avatar
VIEW  
unknown committed
880
      /* lex should contain at least one table */
881
      DBUG_ASSERT(view_tables != 0);
unknown's avatar
VIEW  
unknown committed
882 883

      table->effective_algorithm= VIEW_ALGORITHM_MERGE;
884
      DBUG_PRINT("info", ("algorithm: MERGE"));
885
      table->updatable= (table->updatable_view != 0);
886 887 888 889 890 891 892 893
      table->effective_with_check=
        old_lex->get_effective_with_check(table);

      /* prepare view context */
      lex->select_lex.context.resolve_in_table_list_only(table->ancestor=
                                                         view_tables);
      lex->select_lex.context.outer_context= 0;
      lex->select_lex.context.select_lex= table->select_lex;
unknown's avatar
unknown committed
894 895 896
      lex->select_lex.select_n_having_items+=
        table->select_lex->select_n_having_items;

897 898 899 900 901 902 903 904 905
      /* do not check privileges & hide errors for view underlyings */
      for (SELECT_LEX *sl= lex->all_selects_list;
           sl;
           sl= sl->next_select_in_list())
      {
        sl->context.check_privileges= FALSE;
        sl->context.error_processor= &view_error_processor;
        sl->context.error_processor_data= (void *)table;
      }
unknown's avatar
VIEW  
unknown committed
906
      /*
907 908 909 910
        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
911
      */
912
      for (tbl= lex->select_lex.get_table_list(); tbl; tbl= tbl->next_local)
913 914
        tbl->select_lex= table->select_lex;

915
      {
unknown's avatar
unknown committed
916
        if (view_tables->next_local)
unknown's avatar
unknown committed
917
        {
unknown's avatar
unknown committed
918
          table->multitable_view= TRUE;
unknown's avatar
unknown committed
919 920 921
          if (table->belong_to_view)
           table->belong_to_view->multitable_view= TRUE;
        }
922 923 924 925 926 927 928 929
        /* 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
930 931
        ti.rewind();
        while ((tbl= ti++))
932
        {
unknown's avatar
unknown committed
933 934
          tbl->join_list= &nested_join->join_list;
          tbl->embedding= table;
935 936
        }
      }
unknown's avatar
VIEW  
unknown committed
937

938
      /* Store WHERE clause for post-processing in setup_ancestor */
939
      table->where= view_select->where;
940
      /*
941 942 943 944
        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),
945
        to do this we remember in end_unit the first subquery that has
946
        been already added.
947

948 949
        NOTE: we do not support UNION here, so we take only one select
      */
950
      SELECT_LEX_NODE *end_unit= table->select_lex->slave;
951
      SELECT_LEX_UNIT *next_unit;
952 953
      for (SELECT_LEX_UNIT *unit= lex->select_lex.first_inner_unit();
           unit;
954
           unit= next_unit)
955
      {
956 957
        if (unit == end_unit)
          break;
958 959
        SELECT_LEX_NODE *save_slave= unit->slave;
        next_unit= unit->next_unit();
960 961 962 963
        unit->include_down(table->select_lex);
        unit->slave= save_slave; // fix include_down initialisation
      }

unknown's avatar
VIEW  
unknown committed
964 965 966 967 968 969 970 971 972
      /*
	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;
    }

973
    table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
974
    DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
975
    view_select->linkage= DERIVED_TABLE_TYPE;
976
    table->updatable= 0;
977
    table->effective_with_check= VIEW_CHECK_NONE;
unknown's avatar
unknown committed
978
    old_lex->subqueries= TRUE;
unknown's avatar
VIEW  
unknown committed
979 980 981

    /* SELECT tree link */
    lex->unit.include_down(table->select_lex);
982
    lex->unit.slave= view_select; // fix include_down initialisation
unknown's avatar
VIEW  
unknown committed
983 984 985 986 987 988 989 990

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

ok:
  /* global SELECT list linking */
991
  end= view_select;	// primary SELECT_LEX is always last
unknown's avatar
VIEW  
unknown committed
992 993 994 995 996 997
  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;

998 999 1000
ok2:
  if (arena)
    thd->restore_backup_item_arena(arena, &backup);
unknown's avatar
VIEW  
unknown committed
1001 1002 1003 1004 1005 1006
  thd->lex= old_lex;
  DBUG_RETURN(0);

err:
  if (arena)
    thd->restore_backup_item_arena(arena, &backup);
1007
  delete table->view;
unknown's avatar
VIEW  
unknown committed
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  table->view= 0;	// now it is not VIEW placeholder
  thd->lex= old_lex;
  DBUG_RETURN(1);
}


/*
  drop view

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

  RETURN VALUE
unknown's avatar
unknown committed
1024 1025
    FALSE OK
    TRUE  Error
unknown's avatar
VIEW  
unknown committed
1026
*/
1027

unknown's avatar
unknown committed
1028
bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
unknown's avatar
VIEW  
unknown committed
1029 1030 1031 1032 1033 1034 1035 1036
{
  DBUG_ENTER("mysql_drop_view");
  char path[FN_REFLEN];
  TABLE_LIST *view;
  bool type= 0;

  for (view= views; view; view= view->next_local)
  {
1037
    strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/",
1038
             view->table_name, reg_ext, NullS);
unknown's avatar
VIEW  
unknown committed
1039 1040 1041 1042 1043
    (void) unpack_filename(path, path);
    VOID(pthread_mutex_lock(&LOCK_open));
    if (access(path, F_OK) || (type= (mysql_frm_type(path) != FRMTYPE_VIEW)))
    {
      char name[FN_REFLEN];
1044
      my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
unknown's avatar
VIEW  
unknown committed
1045 1046 1047 1048 1049 1050 1051 1052 1053
      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);
	VOID(pthread_mutex_unlock(&LOCK_open));
	continue;
      }
      if (type)
1054
        my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
unknown's avatar
VIEW  
unknown committed
1055 1056 1057 1058 1059 1060
      else
        my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
      goto err;
    }
    if (my_delete(path, MYF(MY_WME)))
      goto err;
1061
    query_cache_invalidate3(thd, view, 0);
unknown's avatar
unknown committed
1062
    sp_cache_invalidate();
unknown's avatar
VIEW  
unknown committed
1063 1064 1065
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
  send_ok(thd);
unknown's avatar
unknown committed
1066
  DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
1067 1068 1069

err:
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1070
  DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091

}


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

frm_type_enum mysql_frm_type(char *path)
{
  File file;
  char header[10];	//"TYPE=VIEW\n" it is 10 characters
1092
  int length;
unknown's avatar
VIEW  
unknown committed
1093 1094 1095 1096 1097 1098
  DBUG_ENTER("mysql_frm_type");

  if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
  {
    DBUG_RETURN(FRMTYPE_ERROR);
  }
1099
  length= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME));
unknown's avatar
VIEW  
unknown committed
1100
  my_close(file, MYF(MY_WME));
1101 1102
  if (length == (int) MY_FILE_ERROR)
    DBUG_RETURN(FRMTYPE_ERROR);
1103 1104
  if (length < (int) sizeof(header) ||
      !strncmp(header, "TYPE=VIEW\n", sizeof(header)))
1105 1106
    DBUG_RETURN(FRMTYPE_VIEW);
  DBUG_RETURN(FRMTYPE_TABLE);                   // Is probably a .frm table
unknown's avatar
VIEW  
unknown committed
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
}


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

1118
  DESCRIPTION
1119 1120
    If it is VIEW and query have LIMIT clause then check that undertlying
    table of viey contain one of following:
1121 1122 1123 1124 1125
      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
1126 1127 1128 1129 1130 1131 1132
  RETURN
    FALSE   OK
    TRUE    view do not contain key or all fields
*/

bool check_key_in_view(THD *thd, TABLE_LIST *view)
{
1133
  TABLE *table;
1134
  Field_translator *trans, *end_of_trans;
1135
  KEY *key_info, *key_info_end;
1136
  uint i;
unknown's avatar
VIEW  
unknown committed
1137
  DBUG_ENTER("check_key_in_view");
1138

1139
  /*
unknown's avatar
merge  
unknown committed
1140
    we do not support updatable UNIONs in VIEW, so we can check just limit of
1141 1142
    LEX::select_lex
  */
1143 1144 1145
  if ((!view->view && !view->belong_to_view) ||
      thd->lex->sql_command == SQLCOM_INSERT ||
      thd->lex->select_lex.select_limit == 0)
1146
    DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
1147
  table= view->table;
1148 1149
  if (view->belong_to_view)
    view= view->belong_to_view;
1150
  trans= view->field_translation;
1151
  key_info_end= (key_info= table->key_info)+ table->s->keys;
unknown's avatar
VIEW  
unknown committed
1152

1153
  end_of_trans=  view->field_translation_end;
1154
  DBUG_ASSERT(table != 0 && view->field_translation != 0);
unknown's avatar
VIEW  
unknown committed
1155

1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
  {
    /*
      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
    */
    bool save_set_query_id= thd->set_query_id;
    thd->set_query_id= 0;
    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
1167 1168
      {
        thd->set_query_id= save_set_query_id;        
1169
        return TRUE;
unknown's avatar
unknown committed
1170
      }
1171 1172 1173
    }
    thd->set_query_id= save_set_query_id;
  }
1174 1175
  /* 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
1176
  {
1177
    if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
unknown's avatar
VIEW  
unknown committed
1178 1179
    {
      KEY_PART_INFO *key_part= key_info->key_part;
1180 1181 1182 1183
      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
1184
      {
1185 1186
        Field_translator *k;
        for (k= trans; k < end_of_trans; k++)
unknown's avatar
VIEW  
unknown committed
1187
        {
1188
          Item_field *field;
1189
          if ((field= k->item->filed_for_view_update()) &&
1190
              field->field == key_part->field)
unknown's avatar
VIEW  
unknown committed
1191 1192
            break;
        }
1193
        if (k == end_of_trans)
1194 1195 1196
          break;                                // Key is not possible
        if (++key_part == key_part_end)
          DBUG_RETURN(FALSE);                   // Found usable key
unknown's avatar
VIEW  
unknown committed
1197 1198 1199 1200
      }
    }
  }

1201
  DBUG_PRINT("info", ("checking if all fields of table are used"));
unknown's avatar
VIEW  
unknown committed
1202 1203
  /* check all fields presence */
  {
1204
    Field **field_ptr;
1205
    Field_translator *fld;
1206
    for (field_ptr= table->field; *field_ptr; field_ptr++)
unknown's avatar
VIEW  
unknown committed
1207
    {
1208
      for (fld= trans; fld < end_of_trans; fld++)
unknown's avatar
VIEW  
unknown committed
1209
      {
1210
        Item_field *field;
1211
        if ((field= fld->item->filed_for_view_update()) &&
1212
            field->field == *field_ptr)
unknown's avatar
VIEW  
unknown committed
1213 1214
          break;
      }
1215
      if (fld == end_of_trans)                // If field didn't exists
unknown's avatar
VIEW  
unknown committed
1216
      {
1217
        /*
1218
          Keys or all fields of underlying tables are not found => we have
1219 1220
          to check variable updatable_views_with_limit to decide should we
          issue an error or just a warning
1221
        */
1222
        if (thd->variables.updatable_views_with_limit)
unknown's avatar
VIEW  
unknown committed
1223
        {
1224 1225 1226 1227
          /* 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
1228
        }
1229 1230
        /* prohibit update */
        DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
      }
    }
  }
  DBUG_RETURN(FALSE);
}


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

  SYNOPSIS
    insert_view_fields()
1243
    thd       thread handler
unknown's avatar
VIEW  
unknown committed
1244 1245
    list      list for insertion
    view      view for processing
1246 1247

  RETURN
unknown's avatar
unknown committed
1248 1249
    FALSE OK
    TRUE  error (is not sent to cliet)
unknown's avatar
VIEW  
unknown committed
1250 1251
*/

1252
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
unknown's avatar
VIEW  
unknown committed
1253
{
1254
  Field_translator *trans_end;
1255
  Field_translator *trans;
unknown's avatar
VIEW  
unknown committed
1256
  DBUG_ENTER("insert_view_fields");
1257 1258

  if (!(trans= view->field_translation))
unknown's avatar
unknown committed
1259
    DBUG_RETURN(FALSE);
1260
  trans_end= view->field_translation_end;
unknown's avatar
VIEW  
unknown committed
1261

1262
  for (Field_translator *entry= trans; entry < trans_end; entry++)
unknown's avatar
VIEW  
unknown committed
1263
  {
1264
    Item_field *fld;
1265
    if ((fld= entry->item->filed_for_view_update()))
1266
      list->push_back(fld);
1267 1268 1269
    else
    {
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "INSERT");
unknown's avatar
unknown committed
1270
      DBUG_RETURN(TRUE);
1271
    }
unknown's avatar
VIEW  
unknown committed
1272
  }
unknown's avatar
unknown committed
1273
  DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
1274
}
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299

/*
  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);
}