sql_view.cc 32.6 KB
Newer Older
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua 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"
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
21
#include "sp.h"
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
22

23 24
#define MD5_BUFF_LENGTH 33

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
25 26 27
static int mysql_register_view(THD *thd, TABLE_LIST *view,
			       enum_view_create_mode mode);

28 29
const char *updatable_views_with_limit_names[]= { "NO", "YES", NullS };
TYPELIB updatable_views_with_limit_typelib=
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
30
{
31
  array_elements(updatable_views_with_limit_names)-1, "",
monty@mysql.com's avatar
monty@mysql.com committed
32 33
  updatable_views_with_limit_names,
  0
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
34 35 36 37 38 39 40 41 42 43 44 45
};


/*
  Creating/altering VIEW procedure

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

  RETURN VALUE
46 47
     FALSE OK
     TRUE  Error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
48
*/
49

50 51
bool mysql_create_view(THD *thd,
                       enum_view_create_mode mode)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
52 53 54 55 56 57
{
  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;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
58
  TABLE_LIST *tbl;
59 60 61 62
  SELECT_LEX *select_lex= &lex->select_lex;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  SELECT_LEX *sl;
#endif
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
63
  SELECT_LEX_UNIT *unit= &lex->unit;
64
  bool res= FALSE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
65 66
  DBUG_ENTER("mysql_create_view");

67 68 69 70 71 72
  if (lex->proc_list.first ||
      lex->result)
  {
    my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), (lex->result ?
                                             "INTO" :
                                             "PROCEDURE"));
73
    res= TRUE;
74 75 76
    goto err;
  }
  if (lex->derived_tables ||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
77 78
      lex->variables_used || lex->param_list.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
79
    int err= (lex->derived_tables ?
80
              ER_VIEW_SELECT_DERIVED :
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
81 82
              ER_VIEW_SELECT_VARIABLE);
    my_message(err, ER(err), MYF(0));
83
    res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
84 85 86 87
    goto err;
  }

#ifndef NO_EMBEDDED_ACCESS_CHECKS
88 89 90
  /*
    Privilege check for view creation:
    - user have CREATE VIEW privilege on view table
91 92
    - user have DELETE privilege in case of ALTER VIEW or CREATE OR REPLACE
    VIEW
93 94 95 96 97 98 99 100 101
    - have some (SELECT/UPDATE/INSERT/DELETE) privileges on columns of
    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)
    - have SELECT privilege on columns used in expressions of VIEW select
    - 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)
  */
102 103 104 105 106 107 108
  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 &&
       (check_access(thd, DELETE_ACL, view->db, &view->grant.privilege,
                     0, 0) ||
        grant_option && check_grant(thd, DELETE_ACL, view, 0, 1, 0))))
109
    DBUG_RETURN(TRUE);
110
  for (sl= select_lex; sl; sl= sl->next_select())
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
111
  {
112
    for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
113
    {
114
      /*
115
        Ensure that we have some privileges on this table, more strict check
116 117
        will be done on column level after preparation,
      */
monty@mysql.com's avatar
monty@mysql.com committed
118
      if (check_some_access(thd, VIEW_ANY_ACL, tbl))
119
      {
120
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
121
                 "ANY", thd->priv_user, thd->host_or_ip, tbl->table_name);
122
        DBUG_RETURN(TRUE);
123
      }
124 125 126 127
      /*
        Mark this table as a table which will be checked after the prepare
        phase
      */
128
      tbl->table_in_first_from_clause= 1;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
129

130
      /*
131 132 133
        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
134 135 136
      */
      tbl->grant.want_privilege= SELECT_ACL;
      /*
137
        Make sure that all rights are loaded to the TABLE::grant field.
138

139
        tbl->table_name will be correct name of table because VIEWs are
140 141 142
        not opened yet.
      */
      fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
143
                                      tbl->table_name);
144
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
145 146 147 148 149
  }

  if (&lex->select_lex != lex->all_selects_list)
  {
    /* check tables of subqueries */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
150
    for (tbl= tables; tbl; tbl= tbl->next_global)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
151 152 153 154 155 156 157
    {
      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))
        {
158
          res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
159 160 161 162 163 164
          goto err;
        }
      }
    }
  }
  /*
165
    Mark fields for special privilege check ("any" privilege)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
166
  */
167
  for (sl= select_lex; sl; sl= sl->next_select())
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
168
  {
169
    List_iterator_fast<Item> it(sl->item_list);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
170 171 172
    Item *item;
    while ((item= it++))
    {
173 174 175
      Item_field *field;
      if ((field= item->filed_for_view_update()))
        field->any_privileges= 1;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
176 177 178 179
    }
  }
#endif

180 181
  if (open_and_lock_tables(thd, tables))
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
182

183 184 185 186
  /*
    check that tables are not temporary  and this VIEW do not used in query
    (it is possible with ALTERing VIEW)
  */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
187
  for (tbl= tables; tbl; tbl= tbl->next_global)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
188
  {
189
    /* is this table temporary and is not view? */
190
    if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view &&
191
        !tbl->schema_table)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
192 193
    {
      my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
194
      res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
195 196 197
      goto err;
    }

198 199 200
    /* is this table view and the same view which we creates now? */
    if (tbl->view &&
        strcmp(tbl->view_db.str, view->db) == 0 &&
201
        strcmp(tbl->view_name.str, view->table_name) == 0)
202 203
    {
      my_error(ER_NO_SUCH_TABLE, MYF(0), tbl->view_db.str, tbl->view_name.str);
204
      res= TRUE;
205 206 207
      goto err;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
208
    /*
209
      Copy the privileges of the underlying VIEWs which were filled by
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
210
      fill_effective_table_privileges
211
      (they were not copied at derived tables processing)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
212 213 214 215
    */
    tbl->table->grant.privilege= tbl->grant.privilege;
  }

216
  /* prepare select to resolve all fields */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
217
  lex->view_prepare_mode= 1;
218 219 220 221 222 223
  if (unit->prepare(thd, 0, 0))
  {
    /*
      some errors from prepare are reported to user, if is not then
      it will be checked after err: label
    */
224
    res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
225
    goto err;
226
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
227 228 229 230 231 232 233 234

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

    if (lex->view_list.elements != select_lex->item_list.elements)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
237
    {
238 239
      my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0));
      goto err;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
240
    }
241 242
    while ((item= it++, name= nm++))
      item->set_name(name->str, name->length, system_charset_info);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
243 244
  }

245 246 247 248 249
  /* Test absence of duplicates names */
  {
    Item *item;
    List_iterator_fast<Item> it(select_lex->item_list);
    it++;
monty@mysql.com's avatar
monty@mysql.com committed
250
    while ((item= it++))
251 252 253
    {
      Item *check;
      List_iterator_fast<Item> itc(select_lex->item_list);
monty@mysql.com's avatar
monty@mysql.com committed
254
      while ((check= itc++) && check != item)
255 256 257 258
      {
        if (strcmp(item->name, check->name) == 0)
        {
          my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
259
          DBUG_RETURN(TRUE);
260 261 262 263 264
        }
      }
    }
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
265 266
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  /*
267
    Compare/check grants on view with grants of underlying tables
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
268
  */
269
  for (sl= select_lex; sl; sl= sl->next_select())
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
270 271
  {
    char *db= view->db ? view->db : thd->db;
272
    List_iterator_fast<Item> it(sl->item_list);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
273 274
    Item *item;
    fill_effective_table_privileges(thd, &view->grant, db,
275
                                    view->table_name);
276
    while ((item= it++))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
277
    {
278
      Item_field *fld;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
279
      uint priv= (get_column_grant(thd, &view->grant, db,
280
                                    view->table_name, item->name) &
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
281
                  VIEW_ANY_ACL);
282
      if ((fld= item->filed_for_view_update()))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
283 284
      {
        /*
285
          Do we have more privileges on view field then underlying table field?
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
286
        */
287
        if (!fld->field->table->s->tmp_table && (~fld->have_privileges & priv))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
288 289
        {
          /* VIEW column has more privileges */
290 291
          my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
                   "create view", thd->priv_user, thd->host_or_ip, item->name,
292
                   view->table_name);
293
          DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
294 295 296 297 298 299
        }
      }
    }
  }
#endif

300
  if (wait_if_global_read_lock(thd, 0, 0))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
301
  {
302
    res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
303 304 305
    goto err;
  }
  VOID(pthread_mutex_lock(&LOCK_open));
306
  res= mysql_register_view(thd, view, mode);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
307
  VOID(pthread_mutex_unlock(&LOCK_open));
308 309
  if (view->revision != 1)
    query_cache_invalidate3(thd, view, 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
310
  start_waiting_global_read_lock(thd);
311 312
  if (res)
    goto err;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
313 314 315

  send_ok(thd);
  lex->link_first_table_back(view, link_to_local);
monty@mysql.com's avatar
monty@mysql.com committed
316
  DBUG_RETURN(0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
317 318 319 320 321

err:
  thd->proc_info= "end";
  lex->link_first_table_back(view, link_to_local);
  unit->cleanup();
322
  DBUG_RETURN(res || thd->net.report_error);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
323 324 325
}


326 327 328
/* index of revision number in following table */
static const int revision_number_position= 5;
/* index of last required parameter for making view */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
329
static const int required_view_parameters= 7;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
330

331 332 333 334 335 336
/*
  table of VIEW .frm field descriptors

  Note that one should NOT change the order for this, as it's used by
  parse()
*/
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
337
static File_option view_parameters[]=
338
{{{(char*) "query", 5},		offsetof(TABLE_LIST, query),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
339
  FILE_OPTIONS_STRING},
340
 {{(char*) "md5", 3},		offsetof(TABLE_LIST, md5),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
341
  FILE_OPTIONS_STRING},
monty@mysql.com's avatar
monty@mysql.com committed
342
 {{(char*) "updatable", 9},	offsetof(TABLE_LIST, updatable_view),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
343
  FILE_OPTIONS_ULONGLONG},
344
 {{(char*) "algorithm", 9},	offsetof(TABLE_LIST, algorithm),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
345
  FILE_OPTIONS_ULONGLONG},
monty@mysql.com's avatar
monty@mysql.com committed
346
 {{(char*) "with_check_option", 17}, offsetof(TABLE_LIST, with_check),
347
   FILE_OPTIONS_ULONGLONG},
348
 {{(char*) "revision", 8},	offsetof(TABLE_LIST, revision),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
349
  FILE_OPTIONS_REV},
350
 {{(char*) "timestamp", 9},	offsetof(TABLE_LIST, timestamp),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
351
  FILE_OPTIONS_TIMESTAMP},
352
 {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
353
  FILE_OPTIONS_ULONGLONG},
354
 {{(char*) "source", 6},	offsetof(TABLE_LIST, source),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
355
  FILE_OPTIONS_ESTRING},
monty@mysql.com's avatar
monty@mysql.com committed
356
 {{NullS, 0},			0,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
  FILE_OPTIONS_STRING}
};

static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}};


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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
378 379 380
static int mysql_register_view(THD *thd, TABLE_LIST *view,
			       enum_view_create_mode mode)
{
381
  LEX *lex= thd->lex;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
382 383
  char buff[4096];
  String str(buff,(uint32) sizeof(buff), system_charset_info);
384
  char md5[MD5_BUFF_LENGTH];
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
385 386 387 388 389
  bool can_be_merged;
  char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
  LEX_STRING dir, file;
  DBUG_ENTER("mysql_register_view");

390
  /* print query */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
391
  str.length(0);
392 393 394
  {
    ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
    thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
395
    lex->unit.print(&str);
396 397
    thd->variables.sql_mode|= sql_mode;
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
398 399 400
  str.append('\0');
  DBUG_PRINT("VIEW", ("View: %s", str.ptr()));

401
  /* print file name */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
402 403 404 405 406 407 408
  (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;
409
  file.length= (strxnmov(file_buff, FN_REFLEN, view->table_name, reg_ext,
410
                         NullS) - file_buff);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
411
  /* init timestamp */
412
  if (!view->timestamp.str)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
413 414
    view->timestamp.str= view->timestamp_buffer;

415
  /* check old .frm */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
416 417 418
  {
    char path_buff[FN_REFLEN];
    LEX_STRING path;
419
    File_parser *parser;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
420

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
421 422 423 424 425 426 427 428 429
    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);
430
	DBUG_RETURN(-1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
431 432
      }

monty@mysql.com's avatar
monty@mysql.com committed
433
      if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0)))
434 435 436 437
	DBUG_RETURN(1);

      if (!parser->ok() ||
          strncmp("VIEW", parser->type()->str, parser->type()->length))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
438
      {
439
        my_error(ER_WRONG_OBJECT, MYF(0),
440
                 (view->db ? view->db : thd->db), view->table_name, "VIEW");
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
441
        DBUG_RETURN(-1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
442
      }
443 444 445

      /*
        read revision number
446

447
        TODO: read dependence list, too, to process cascade/restrict
448 449
        TODO: special cascade/restrict procedure for alter?
      */
monty@mysql.com's avatar
monty@mysql.com committed
450
      if (parser->parse((gptr)view, thd->mem_root,
451
                        view_parameters + revision_number_position, 1))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
452
      {
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
453
        DBUG_RETURN(thd->net.report_error? -1 : 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
454 455 456 457 458 459 460
      }
    }
    else
    {
      if (mode == VIEW_ALTER)
      {
	my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias);
461
	DBUG_RETURN(-1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
462 463 464
      }
    }
  }
465
  /* fill structure */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
466 467 468 469 470 471 472 473
  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;
474 475 476
  can_be_merged= lex->can_be_merged();
  if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
      !lex->can_be_merged())
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
477 478 479
  {
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
                 ER(ER_WARN_VIEW_MERGE));
480
    lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
481
  }
482 483
  view->algorithm= lex->create_view_algorithm;
  view->with_check= lex->create_view_check;
484
  if ((view->updatable_view= (can_be_merged &&
monty@mysql.com's avatar
monty@mysql.com committed
485
                              view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
486
  {
487
    /* TODO: change here when we will support UNIONs */
488
    for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first;
489 490
	 tbl;
	 tbl= tbl->next_local)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
491
    {
492
      if ((tbl->view && !tbl->updatable_view) || tbl->schema_table)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
493
      {
494 495 496 497
	view->updatable_view= 0;
	break;
      }
      for (TABLE_LIST *up= tbl; up; up= up->embedding)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
498
      {
499 500 501 502 503
	if (up->outer_join)
	{
	  view->updatable_view= 0;
	  goto loop_out;
	}
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
504 505 506
      }
    }
  }
507
loop_out:
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
  /*
    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,
523
				lex->query_tables->table_name))
524 525 526 527
  {
    view->updatable_view= 0;
  }

528 529 530
  if (view->with_check != VIEW_CHECK_NONE &&
      !view->updatable_view)
  {
531
    my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->table_name);
532 533 534
    DBUG_RETURN(-1);
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
535 536 537
  if (sql_create_definition_file(&dir, &file, view_file_type,
				 (gptr)view, view_parameters, 3))
  {
538
    DBUG_RETURN(thd->net.report_error? -1 : 1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
539 540 541 542 543 544 545 546 547 548 549 550
  }
  DBUG_RETURN(0);
}


/*
  read VIEW .frm and create structures

  SYNOPSIS
    mysql_make_view()
    parser		- parser object;
    table		- TABLE_LIST structure for filling
551 552

  RETURN
553 554
    0 ok
    1 error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
555
*/
556

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
557 558 559 560
my_bool
mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
  DBUG_ENTER("mysql_make_view");
561
  DBUG_PRINT("info", ("table=%p (%s)", table, table->table_name));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
562 563 564 565

  if (table->view)
  {
    DBUG_PRINT("info",
566
               ("VIEW %s.%s is already processed on previous PS/SP execution",
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
567 568 569 570
                table->view_db.str, table->view_name.str));
    DBUG_RETURN(0);
  }

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
571
  SELECT_LEX *end;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
572 573
  THD *thd= current_thd;
  LEX *old_lex= thd->lex, *lex;
574
  SELECT_LEX *view_select;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
575 576 577 578 579 580 581
  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).
  */
  Item_arena *arena= thd->current_arena, backup;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
582
  if (arena->is_conventional())
583 584
    arena= 0;
  else
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
585 586 587
    thd->set_n_backup_item_arena(arena, &backup);

  /* init timestamp */
588
  if (!table->timestamp.str)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
589 590 591 592 593
    table->timestamp.str= table->timestamp_buffer;
  /*
    TODO: when VIEWs will be stored in cache, table mem_root should
    be used here
  */
monty@mysql.com's avatar
monty@mysql.com committed
594
  if (parser->parse((gptr)table, thd->mem_root, view_parameters,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
595
                    required_view_parameters))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
596 597 598 599 600 601 602 603
    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;
604 605
  table->view_name.str= table->table_name;
  table->view_name.length= table->table_name_length;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
606

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
607
  /*TODO: md5 test here and warning if it is differ */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
608

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
609 610 611 612 613 614
  /*
    TODO: TABLE mem root should be used here when VIEW will be stored in
    TABLE cache

    now Lex placed in statement memory
  */
monty@mysql.com's avatar
monty@mysql.com committed
615
  table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
monty@mysql.com's avatar
monty@mysql.com committed
616
  lex_start(thd, (uchar*)table->query.str, table->query.length);
617
  view_select= &lex->select_lex;
618 619 620
  /* Only if we're not in the pre-open phase */
  if (!thd->shortcut_make_view)
    view_select->select_number= ++thd->select_number;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
  old_lex->derived_tables|= DERIVED_VIEW;
  {
    ulong options= thd->options;
    /* 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
    */
    thd->options&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
                     MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
650
    CHARSET_INFO *save_cs= thd->variables.character_set_client;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
651
    thd->variables.character_set_client= system_charset_info;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
652
    res= yyparse((void *)thd);
653
    thd->variables.character_set_client= save_cs;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
654 655 656 657
    thd->options= options;
  }
  if (!res && !thd->is_fatal_error)
  {
658 659 660
    TABLE_LIST *top_view= (table->belong_to_view ?
                           table->belong_to_view :
                           table);
661 662
    TABLE_LIST *view_tables= lex->query_tables;
    TABLE_LIST *view_tables_tail= 0;
663
    TABLE_LIST *tbl;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
664

665
    /* move SP to main LEX */
666
    if (lex->spfuns.records)
667 668
      sp_merge_hash(&old_lex->spfuns, &lex->spfuns);

669
    /* cleanup LEX */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
670 671
    if (lex->spfuns.array.buffer)
      hash_free(&lex->spfuns);
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
    if (lex->spprocs.array.buffer)
      hash_free(&lex->spprocs);
    if (lex->sptabs.array.buffer)
      hash_free(&lex->sptabs);

    /* If we're pre-opening tables to find SPs and tables we need
       not go any further; doing so will cause an infinite loop. */
    if (thd->shortcut_make_view)
    {
      extern bool
	sp_merge_table_list(THD *thd, HASH *h, TABLE_LIST *table,
			    LEX *lex_for_tmp_check = 0);

      sp_merge_table_list(thd, &old_lex->sptabs, view_tables);
      goto ok;
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
688

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
689 690
    /*
      check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
691
      underlying tables
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
692
    */
693
    if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
694
    {
695
      if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
696
          check_table_access(thd, SHOW_VIEW_ACL, table, 1))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
697
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
698
        my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
699 700 701
        goto err;
      }
    }
702 703 704 705 706
    else if (old_lex->sql_command == SQLCOM_SHOW_CREATE)
    {
      if (check_table_access(thd, SHOW_VIEW_ACL, table, 0))
        goto err;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
707

708 709 710 711 712 713 714 715 716 717 718 719
    /*
      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;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
720 721 722 723
    /* 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 */
724
    if (view_select->options & OPTION_TO_QUERY_CACHE)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
725 726
      old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;

727 728 729 730 731 732 733 734 735 736 737
    /*
      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)
      {
738
        view_tables_tail->next_global= table->next_global;
739 740 741 742 743 744 745 746 747 748
        table->next_global->prev_global= &view_tables_tail->next_global;
      }
      else
      {
        lex->query_tables_last= &view_tables_tail->next_global;
      }
      view_tables->prev_global= &table->next_global;
      table->next_global= view_tables;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
749 750 751
    /*
      check MERGE algorithm ability
      - algorithm is not explicit TEMPORARY TABLE
752
      - VIEW SELECT allow merging
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
753 754
      - VIEW used in subquery or command support MERGE algorithm
    */
755
    if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
756 757
	lex->can_be_merged() &&
        (table->select_lex->master_unit() != &old_lex->unit ||
758 759
         old_lex->can_use_merged()) &&
        !old_lex->can_not_use_merged())
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
760 761
    {
      /* lex should contain at least one table */
762
      DBUG_ASSERT(view_tables != 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
763 764

      table->effective_algorithm= VIEW_ALGORITHM_MERGE;
765
      DBUG_PRINT("info", ("algorithm: MERGE"));
766
      table->updatable= (table->updatable_view != 0);
767
      table->effective_with_check= (uint8)table->with_check;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
768

769 770
      table->ancestor= view_tables;

bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
771
      /* next table should include SELECT_LEX under this table SELECT_LEX */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
772
      table->ancestor->select_lex= table->select_lex;
773

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
774
      /*
775 776
        Process upper level tables of view. As far as we do noy suport union
        here we can go through local tables of view most upper SELECT
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
777
      */
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
      for(tbl= (TABLE_LIST*)view_select->table_list.first;
          tbl;
          tbl= tbl->next_local)
      {
        /*
          move lock type (TODO: should we issue error in case of TMPTABLE
          algorithm and non-read locking)?
        */
        tbl->lock_type= table->lock_type;
      }

      /* multi table view */
      if (view_tables->next_local)
      {
        /* 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 */
        {
          List_iterator_fast<TABLE_LIST> ti(nested_join->join_list);
monty@mysql.com's avatar
monty@mysql.com committed
802
          while ((tbl= ti++))
803 804 805 806 807 808
          {
            tbl->join_list= &nested_join->join_list;
            tbl->embedding= table;
          }
        }
      }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
809

810
      /* Store WHERE clause for post-processing in setup_ancestor */
811
      table->where= view_select->where;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
812

813 814 815 816 817 818 819 820 821 822 823 824 825 826
      /*
        Add subqueries units to SELECT in which we merging current view.

        NOTE: we do not support UNION here, so we take only one select
      */
      for (SELECT_LEX_UNIT *unit= lex->select_lex.first_inner_unit();
           unit;
           unit= unit->next_unit())
      {
        SELECT_LEX_NODE *save_slave= unit->slave;
        unit->include_down(table->select_lex);
        unit->slave= save_slave; // fix include_down initialisation
      }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
827 828 829 830 831 832 833 834 835
      /*
	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;
    }

836
    table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
837
    DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
838
    view_select->linkage= DERIVED_TABLE_TYPE;
839
    table->updatable= 0;
840
    table->effective_with_check= VIEW_CHECK_NONE;
monty@mysql.com's avatar
monty@mysql.com committed
841
    old_lex->subqueries= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
842 843 844

    /* SELECT tree link */
    lex->unit.include_down(table->select_lex);
845
    lex->unit.slave= view_select; // fix include_down initialisation
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
846 847 848 849 850 851 852 853 854 855

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

ok:
  if (arena)
    thd->restore_backup_item_arena(arena, &backup);
  /* global SELECT list linking */
856
  end= view_select;	// primary SELECT_LEX is always last
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
  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;

  thd->lex= old_lex;
  DBUG_RETURN(0);

err:
  if (arena)
    thd->restore_backup_item_arena(arena, &backup);
  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
885 886
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
887
*/
888

889
bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
890 891 892 893 894 895 896 897
{
  DBUG_ENTER("mysql_drop_view");
  char path[FN_REFLEN];
  TABLE_LIST *view;
  bool type= 0;

  for (view= views; view; view= view->next_local)
  {
898
    strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/",
899
             view->table_name, reg_ext, NullS);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
900 901 902 903 904
    (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];
905
      my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
906 907 908 909 910 911 912 913 914
      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)
915
        my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
916 917 918 919 920 921
      else
        my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
      goto err;
    }
    if (my_delete(path, MYF(MY_WME)))
      goto err;
922
    query_cache_invalidate3(thd, view, 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
923 924 925
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
  send_ok(thd);
926
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
927 928 929

err:
  VOID(pthread_mutex_unlock(&LOCK_open));
930
  DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

}


/*
  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
952
  int length;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
953 954 955 956 957 958
  DBUG_ENTER("mysql_frm_type");

  if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
  {
    DBUG_RETURN(FRMTYPE_ERROR);
  }
959
  length= my_read(file, (byte*) header, sizeof(header), MYF(MY_WME));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
960
  my_close(file, MYF(MY_WME));
961 962
  if (length == (int) MY_FILE_ERROR)
    DBUG_RETURN(FRMTYPE_ERROR);
963 964
  if (length < (int) sizeof(header) ||
      !strncmp(header, "TYPE=VIEW\n", sizeof(header)))
965 966
    DBUG_RETURN(FRMTYPE_VIEW);
  DBUG_RETURN(FRMTYPE_TABLE);                   // Is probably a .frm table
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
967 968 969 970 971 972 973 974 975 976 977
}


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

978
  DESCRIPTION
979 980
    If it is VIEW and query have LIMIT clause then check that undertlying
    table of viey contain one of following:
981 982 983 984 985
      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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
986 987 988 989 990 991 992
  RETURN
    FALSE   OK
    TRUE    view do not contain key or all fields
*/

bool check_key_in_view(THD *thd, TABLE_LIST *view)
{
993
  TABLE *table;
994
  Field_translator *trans;
995 996
  KEY *key_info, *key_info_end;
  uint i, elements_in_view;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
997
  DBUG_ENTER("check_key_in_view");
998

999
  /*
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1000
    we do not support updatable UNIONs in VIEW, so we can check just limit of
1001 1002
    LEX::select_lex
  */
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1003
  if ((!view->view && !view->belong_to_view) || thd->lex->sql_command == SQLCOM_INSERT ||
1004
      thd->lex->select_lex.select_limit == HA_POS_ERROR)
1005
    DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
1006
  table= view->table;
1007 1008
  if (view->belong_to_view)
    view= view->belong_to_view;
1009
  trans= view->field_translation;
1010
  key_info_end= (key_info= table->key_info)+ table->s->keys;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1011

1012
  elements_in_view= view->view->select_lex.item_list.elements;
1013
  DBUG_ASSERT(table != 0 && view->field_translation != 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1014

1015 1016
  /* Loop over all keys to see if a unique-not-null key is used */
  for (;key_info != key_info_end ; key_info++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1017
  {
1018
    if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1019 1020
    {
      KEY_PART_INFO *key_part= key_info->key_part;
1021 1022 1023 1024
      KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;

      /* check that all key parts are used */
      for (;;)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1025
      {
1026 1027
        uint k;
        for (k= 0; k < elements_in_view; k++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1028
        {
1029
          Item_field *field;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1030
          if ((field= trans[k].item->filed_for_view_update()) &&
1031
              field->field == key_part->field)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1032 1033
            break;
        }
1034 1035 1036 1037
        if (k == elements_in_view)
          break;                                // Key is not possible
        if (++key_part == key_part_end)
          DBUG_RETURN(FALSE);                   // Found usable key
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1038 1039 1040 1041
      }
    }
  }

1042
  DBUG_PRINT("info", ("checking if all fields of table are used"));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1043 1044
  /* check all fields presence */
  {
1045 1046
    Field **field_ptr;
    for (field_ptr= table->field; *field_ptr; field_ptr++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1047
    {
1048
      for (i= 0; i < elements_in_view; i++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1049
      {
1050
        Item_field *field;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1051
        if ((field= trans[i].item->filed_for_view_update()) &&
1052
            field->field == *field_ptr)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1053 1054
          break;
      }
1055
      if (i == elements_in_view)                // If field didn't exists
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1056
      {
1057
        /*
1058
          Keys or all fields of underlying tables are not foud => we have
1059 1060
          to check variable updatable_views_with_limit to decide should we
          issue an error or just a warning
1061
        */
1062
        if (thd->variables.updatable_views_with_limit)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1063
        {
1064 1065 1066 1067
          /* 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);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1068
        }
1069 1070
        /* prohibit update */
        DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084
      }
    }
  }
  DBUG_RETURN(FALSE);
}


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

  SYNOPSIS
    insert_view_fields()
    list      list for insertion
    view      view for processing
1085 1086

  RETURN
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1087 1088
    FALSE OK
    TRUE  error (is not sent to cliet)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1089 1090
*/

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1091
bool insert_view_fields(List<Item> *list, TABLE_LIST *view)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1092
{
1093
  uint elements_in_view= view->view->select_lex.item_list.elements;
1094
  Field_translator *trans;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1095
  DBUG_ENTER("insert_view_fields");
1096 1097

  if (!(trans= view->field_translation))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1098
    DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1099

1100
  for (uint i= 0; i < elements_in_view; i++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1101
  {
1102
    Item_field *fld;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1103
    if ((fld= trans[i].item->filed_for_view_update()))
1104
      list->push_back(fld);
1105 1106 1107
    else
    {
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "INSERT");
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1108
      DBUG_RETURN(TRUE);
1109
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1110
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1111
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1112
}
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

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