sql_view.cc 32.3 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 21
/* 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_acl.h"
#include "sql_select.h"
#include "parse_file.h"
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
22
#include "sp.h"
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
23

24 25
#define MD5_BUFF_LENGTH 33

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

29 30
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
31
{
32
  array_elements(updatable_views_with_limit_names)-1, "",
monty@mysql.com's avatar
monty@mysql.com committed
33 34
  updatable_views_with_limit_names,
  0
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
35 36 37 38 39 40 41 42 43 44 45 46
};


/*
  Creating/altering VIEW procedure

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

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

51 52
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
53 54 55 56 57 58
{
  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
59
  TABLE_LIST *tbl;
60 61 62 63
  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
64
  SELECT_LEX_UNIT *unit= &lex->unit;
65
  bool res= FALSE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
66 67
  DBUG_ENTER("mysql_create_view");

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

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

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

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

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

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

184 185 186 187
  /*
    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
188
  for (tbl= tables; tbl; tbl= tbl->next_global)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
189
  {
190
    /* is this table temporary and is not view? */
191 192
    if (tbl->table->tmp_table != NO_TMP_TABLE && !tbl->view &&
        !tbl->schema_table)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
193 194
    {
      my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
195
      res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
196 197 198
      goto err;
    }

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
209
    /*
210
      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
211
      fill_effective_table_privileges
212
      (they were not copied at derived tables processing)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
213 214 215 216
    */
    tbl->table->grant.privilege= tbl->grant.privilege;
  }

217
  /* prepare select to resolve all fields */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
218
  lex->view_prepare_mode= 1;
219 220 221 222 223 224
  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
    */
225
    res= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
226
    goto err;
227
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
228 229 230 231 232 233 234 235

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

    if (lex->view_list.elements != select_lex->item_list.elements)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
238
    {
239 240
      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
241
    }
242 243
    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
244 245
  }

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

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

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

  send_ok(thd);
  lex->link_first_table_back(view, link_to_local);
  return 0;

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


325 326 327
/* 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
328
static const int required_view_parameters= 7;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
329

330 331 332 333 334 335
/*
  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
336
static File_option view_parameters[]=
337
{{{(char*) "query", 5},		offsetof(TABLE_LIST, query),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
338
  FILE_OPTIONS_STRING},
339
 {{(char*) "md5", 3},		offsetof(TABLE_LIST, md5),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
340
  FILE_OPTIONS_STRING},
monty@mysql.com's avatar
monty@mysql.com committed
341
 {{(char*) "updatable", 9},	offsetof(TABLE_LIST, updatable_view),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
342
  FILE_OPTIONS_ULONGLONG},
343
 {{(char*) "algorithm", 9},	offsetof(TABLE_LIST, algorithm),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
344
  FILE_OPTIONS_ULONGLONG},
monty@mysql.com's avatar
monty@mysql.com committed
345
 {{(char*) "with_check_option", 17}, offsetof(TABLE_LIST, with_check),
346
   FILE_OPTIONS_ULONGLONG},
347
 {{(char*) "revision", 8},	offsetof(TABLE_LIST, revision),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
348
  FILE_OPTIONS_REV},
349
 {{(char*) "timestamp", 9},	offsetof(TABLE_LIST, timestamp),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
350
  FILE_OPTIONS_TIMESTAMP},
351
 {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
352
  FILE_OPTIONS_ULONGLONG},
353
 {{(char*) "source", 6},	offsetof(TABLE_LIST, source),
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
354
  FILE_OPTIONS_ESTRING},
monty@mysql.com's avatar
monty@mysql.com committed
355
 {{NullS, 0},			0,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
  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
*/
376

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

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

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

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

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

monty@mysql.com's avatar
monty@mysql.com committed
432
      if (!(parser= sql_parse_prepare(&path, thd->mem_root, 0)))
433 434 435 436
	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
437
      {
438 439
        my_error(ER_WRONG_OBJECT, MYF(0),
                 (view->db ? view->db : thd->db), view->real_name, "VIEW");
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
440
        DBUG_RETURN(-1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
441
      }
442 443 444

      /*
        read revision number
445

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

527 528 529
  if (view->with_check != VIEW_CHECK_NONE &&
      !view->updatable_view)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
530
    my_error(ER_VIEW_NONUPD_CHECK, MYF(0), view->db, view->real_name);
531 532 533
    DBUG_RETURN(-1);
  }

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


/*
  read VIEW .frm and create structures

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
556 557 558 559 560 561 562 563
my_bool
mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
  DBUG_ENTER("mysql_make_view");

  if (table->view)
  {
    DBUG_PRINT("info",
564
               ("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
565 566 567 568
                table->view_db.str, table->view_name.str));
    DBUG_RETURN(0);
  }

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

  /* init timestamp */
586
  if (!table->timestamp.str)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
587 588 589 590 591
    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
592
  if (parser->parse((gptr)table, thd->mem_root, view_parameters,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
593
                    required_view_parameters))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
594 595 596 597 598 599 600 601 602 603 604
    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;
  table->view_name.str= table->real_name;
  table->view_name.length= table->real_name_length;

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
605
  /*TODO: md5 test here and warning if it is differ */
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 608 609 610 611 612
  /*
    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
613
  table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
monty@mysql.com's avatar
monty@mysql.com committed
614
  lex_start(thd, (uchar*)table->query.str, table->query.length);
615 616
  view_select= &lex->select_lex;
  view_select->select_number= ++thd->select_number;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
617 618 619 620 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
  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);
646
    CHARSET_INFO *save_cs= thd->variables.character_set_client;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
647
    thd->variables.character_set_client= system_charset_info;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
648
    res= yyparse((void *)thd);
649
    thd->variables.character_set_client= save_cs;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
650 651 652 653
    thd->options= options;
  }
  if (!res && !thd->is_fatal_error)
  {
654 655 656
    TABLE_LIST *top_view= (table->belong_to_view ?
                           table->belong_to_view :
                           table);
657 658
    TABLE_LIST *view_tables= lex->query_tables;
    TABLE_LIST *view_tables_tail= 0;
659
    TABLE_LIST *tbl;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
660

661 662 663 664
    if (lex->spfuns.records)
    {
      /* move SP to main LEX */
      sp_merge_funs(old_lex, lex);
665
      /* open mysq.proc for functions which are not in cache */
666 667 668 669 670 671 672 673 674 675
      if (old_lex->proc_table == 0 &&
          (old_lex->proc_table=
           (TABLE_LIST*)thd->calloc(sizeof(TABLE_LIST))) != 0)
      {
        TABLE_LIST *table= old_lex->proc_table;
        table->db= (char*)"mysql";
        table->db_length= 5;
        table->real_name= table->alias= (char*)"proc";
        table->real_name_length= 4;
        table->cacheable_table= 1;
676
        old_lex->add_to_query_tables(table);
677 678
      }
    }
679
    /* cleanup LEX */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
680 681
    if (lex->spfuns.array.buffer)
      hash_free(&lex->spfuns);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
682

683 684 685 686
    /*
      mark to avoid temporary table using and put view reference and find
      last view table
    */
687
    for (tbl= view_tables;
688 689
         tbl;
         tbl= (view_tables_tail= tbl)->next_global)
690
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
691
      tbl->skip_temporary= 1;
692 693
      tbl->belong_to_view= top_view;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
694 695 696

    /*
      check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
697
      underlying tables
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
698
    */
699
    if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
700
    {
701
      if (check_table_access(thd, SELECT_ACL, view_tables, 1) &&
702
          check_table_access(thd, SHOW_VIEW_ACL, table, 1))
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
703
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
704
        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
705 706 707
        goto err;
      }
    }
708 709 710 711 712
    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
713 714 715 716 717

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

721 722 723 724 725 726 727 728 729 730 731
    /*
      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)
      {
732
        view_tables_tail->next_global= table->next_global;
733 734 735 736 737 738 739 740 741 742
        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
743 744 745
    /*
      check MERGE algorithm ability
      - algorithm is not explicit TEMPORARY TABLE
746
      - VIEW SELECT allow merging
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
747 748
      - VIEW used in subquery or command support MERGE algorithm
    */
749
    if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
750 751
	lex->can_be_merged() &&
        (table->select_lex->master_unit() != &old_lex->unit ||
752 753
         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
754 755
    {
      /* lex should contain at least one table */
756
      DBUG_ASSERT(view_tables != 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
757 758

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

763 764
      table->ancestor= view_tables;

bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
765
      /* 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
766
      table->ancestor->select_lex= table->select_lex;
767

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
768
      /*
769 770
        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
771
      */
772 773 774 775 776 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 802
      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);
          while(tbl= ti++)
          {
            tbl->join_list= &nested_join->join_list;
            tbl->embedding= table;
          }
        }
      }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
803

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

807 808 809 810 811 812 813 814 815 816 817 818 819 820
      /*
        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
821 822 823 824 825 826 827 828 829
      /*
	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;
    }

830
    table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
831
    DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
832
    view_select->linkage= DERIVED_TABLE_TYPE;
833
    table->updatable= 0;
834
    table->effective_with_check= VIEW_CHECK_NONE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
835 836 837

    /* SELECT tree link */
    lex->unit.include_down(table->select_lex);
838
    lex->unit.slave= view_select; // fix include_down initialisation
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
839 840 841 842 843 844 845 846 847 848

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

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

882
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
883 884 885 886 887 888 889 890
{
  DBUG_ENTER("mysql_drop_view");
  char path[FN_REFLEN];
  TABLE_LIST *view;
  bool type= 0;

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

err:
  VOID(pthread_mutex_unlock(&LOCK_open));
922
  DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943

}


/*
  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
944
  int length;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
945 946 947 948 949 950
  DBUG_ENTER("mysql_frm_type");

  if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
  {
    DBUG_RETURN(FRMTYPE_ERROR);
  }
monty@mysql.com's avatar
monty@mysql.com committed
951
  length= my_read(file, (byte*) header, 10, MYF(MY_WME));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
952
  my_close(file, MYF(MY_WME));
953 954 955 956 957
  if (length == (int) MY_FILE_ERROR)
    DBUG_RETURN(FRMTYPE_ERROR);
  if (!strncmp(header, "TYPE=VIEW\n", 10))
    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
958 959 960 961 962 963 964 965 966 967 968
}


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

969
  DESCRIPTION
970 971
    If it is VIEW and query have LIMIT clause then check that undertlying
    table of viey contain one of following:
972 973 974 975 976
      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
977 978 979 980 981 982 983
  RETURN
    FALSE   OK
    TRUE    view do not contain key or all fields
*/

bool check_key_in_view(THD *thd, TABLE_LIST *view)
{
984
  TABLE *table;
985
  Field_translator *trans;
986 987
  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
988
  DBUG_ENTER("check_key_in_view");
989

990
  /*
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
991
    we do not support updatable UNIONs in VIEW, so we can check just limit of
992 993
    LEX::select_lex
  */
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
994
  if ((!view->view && !view->belong_to_view) || thd->lex->sql_command == SQLCOM_INSERT ||
995
      thd->lex->select_lex.select_limit == HA_POS_ERROR)
996
    DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
997
  table= view->table;
998 999
  if (view->belong_to_view)
    view= view->belong_to_view;
1000 1001
  trans= view->field_translation;
  key_info_end= (key_info= table->key_info)+ table->keys;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1002

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

1006 1007
  /* 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
1008
  {
1009
    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
1010 1011
    {
      KEY_PART_INFO *key_part= key_info->key_part;
1012 1013 1014 1015
      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
1016
      {
1017 1018
        uint k;
        for (k= 0; k < elements_in_view; k++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1019
        {
1020
          Item_field *field;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1021
          if ((field= trans[k].item->filed_for_view_update()) &&
1022
              field->field == key_part->field)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1023 1024
            break;
        }
1025 1026 1027 1028
        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
1029 1030 1031 1032
      }
    }
  }

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


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

  SYNOPSIS
    insert_view_fields()
    list      list for insertion
    view      view for processing
1076 1077

  RETURN
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1078 1079
    FALSE OK
    TRUE  error (is not sent to cliet)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1080 1081
*/

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1082
bool insert_view_fields(List<Item> *list, TABLE_LIST *view)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1083
{
1084
  uint elements_in_view= view->view->select_lex.item_list.elements;
1085
  Field_translator *trans;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1086
  DBUG_ENTER("insert_view_fields");
1087 1088

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

1091
  for (uint i= 0; i < elements_in_view; i++)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1092
  {
1093
    Item_field *fld;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1094
    if ((fld= trans[i].item->filed_for_view_update()))
1095
      list->push_back(fld);
1096 1097 1098
    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
1099
      DBUG_RETURN(TRUE);
1100
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1101
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1102
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1103
}
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128

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