mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
MDEV-37260 Implicitly named query blocks, CREATE VIEW AS supports hints
Renames variables, other cleanup, in several VIEW functions to aid readability.
This commit is contained in:
committed by
Dave Gosselin
parent
49c3c2ab36
commit
2396534356
296
sql/sql_view.cc
296
sql/sql_view.cc
@@ -1004,13 +1004,13 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state,
|
||||
/*
|
||||
View definition query -- a SELECT statement that fully defines view. It
|
||||
is generated from the Item-tree built from the original (specified by
|
||||
the user) query. The idea is that generated query should eliminates all
|
||||
ambiguities and fix view structure at CREATE-time (once for all).
|
||||
the user) query. The idea is that generated query should eliminate all
|
||||
ambiguities.
|
||||
Item::print() virtual operation is used to generate view definition
|
||||
query.
|
||||
|
||||
INFORMATION_SCHEMA query (IS query) -- a SQL statement describing a
|
||||
view that is shown in INFORMATION_SCHEMA. Basically, it is 'view
|
||||
The INFORMATION_SCHEMA query is a SQL statement describing a
|
||||
view as shown in the INFORMATION_SCHEMA database. It is the 'view
|
||||
definition query' with text literals converted to UTF8 and without
|
||||
character set introducers.
|
||||
|
||||
@@ -1021,13 +1021,14 @@ static int mysql_register_view(THD *thd, DDL_LOG_STATE *ddl_log_state,
|
||||
CREATE VIEW v1(x, y) AS SELECT * FROM t1;
|
||||
Generated query:
|
||||
SELECT a AS x, b AS y FROM t1;
|
||||
IS query:
|
||||
INFORMATION_SCHEMA query:
|
||||
SELECT a AS x, b AS y FROM t1;
|
||||
|
||||
View definition query is stored in the client character set.
|
||||
*/
|
||||
StringBuffer<4096> view_query(thd->charset());
|
||||
StringBuffer<4096> is_query(system_charset_info);
|
||||
constexpr const size_t BUFF_4K= 4096;
|
||||
StringBuffer<BUFF_4K> view_query(thd->charset());
|
||||
StringBuffer<BUFF_4K> is_query(system_charset_info); // information_schema
|
||||
|
||||
char md5[MD5_BUFF_LENGTH];
|
||||
bool can_be_merged;
|
||||
@@ -1315,34 +1316,35 @@ bool mariadb_view_version_get(TABLE_SHARE *share)
|
||||
|
||||
@param[in] thd Thread handler
|
||||
@param[in] share Share object of view
|
||||
@param[in] table TABLE_LIST structure for filling
|
||||
@param[in] view_table_alias [OUT] The TABLE_LIST for VIEW as named
|
||||
and referenced in the parent query (pq).
|
||||
@param[in] open_view_no_parse Flag to indicate open view but
|
||||
do not parse.
|
||||
|
||||
@return false-in case of success, true-in case of error.
|
||||
*/
|
||||
bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *view_table_alias,
|
||||
bool open_view_no_parse)
|
||||
{
|
||||
SELECT_LEX_NODE *end;
|
||||
SELECT_LEX *UNINIT_VAR(view_select);
|
||||
LEX *old_lex, *lex;
|
||||
LEX *parent_query_lex, *view_query_lex;
|
||||
Query_arena *arena, backup;
|
||||
TABLE_LIST *top_view= table->top_table();
|
||||
TABLE_LIST *top_view= view_table_alias->top_table();
|
||||
bool UNINIT_VAR(parse_status);
|
||||
bool result, view_is_mergeable;
|
||||
TABLE_LIST *UNINIT_VAR(view_main_select_tables);
|
||||
DBUG_ENTER("mysql_make_view");
|
||||
DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name.str));
|
||||
DBUG_PRINT("info", ("table: %p (%s)", view_table_alias, view_table_alias->table_name.str));
|
||||
|
||||
if (table->required_type == TABLE_TYPE_NORMAL)
|
||||
if (view_table_alias->required_type == TABLE_TYPE_NORMAL)
|
||||
{
|
||||
my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str,
|
||||
"BASE TABLE");
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
|
||||
if (table->view)
|
||||
if (view_table_alias->view)
|
||||
{
|
||||
/*
|
||||
It's an execution of a PS/SP and the view has already been unfolded
|
||||
@@ -1354,13 +1356,13 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
when this information is changed is execution of UPDATE on a view, but
|
||||
the original want_access is restored in its end.
|
||||
*/
|
||||
if (!table->prelocking_placeholder && table->prepare_security(thd))
|
||||
if (!view_table_alias->prelocking_placeholder && view_table_alias->prepare_security(thd))
|
||||
{
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_PRINT("info",
|
||||
("VIEW %s.%s is already processed on previous PS/SP execution",
|
||||
table->view_db.str, table->view_name.str));
|
||||
view_table_alias->view_db.str, view_table_alias->view_name.str));
|
||||
|
||||
/*
|
||||
Clear old variables in the TABLE_LIST that could be left from an old view
|
||||
@@ -1368,27 +1370,27 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
in which case the reinit call wasn't done.
|
||||
See MDEV-6668 for details.
|
||||
*/
|
||||
mysql_handle_single_derived(thd->lex, table, DT_REINIT);
|
||||
mysql_handle_single_derived(thd->lex, view_table_alias, DT_REINIT);
|
||||
|
||||
DEBUG_SYNC(thd, "after_cached_view_opened");
|
||||
DBUG_ASSERT(share->tabledef_version.length);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
if (table->index_hints && table->index_hints->elements)
|
||||
if (view_table_alias->index_hints && view_table_alias->index_hints->elements)
|
||||
{
|
||||
my_error(ER_KEY_DOES_NOT_EXISTS, MYF(0),
|
||||
table->index_hints->head()->key_name.str, table->table_name.str);
|
||||
view_table_alias->index_hints->head()->key_name.str, view_table_alias->table_name.str);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* check loop via view definition */
|
||||
for (TABLE_LIST *precedent= table->referencing_view;
|
||||
for (TABLE_LIST *precedent= view_table_alias->referencing_view;
|
||||
precedent;
|
||||
precedent= precedent->referencing_view)
|
||||
{
|
||||
if (precedent->view_name.streq(table->table_name) &&
|
||||
precedent->view_db.streq(table->db))
|
||||
if (precedent->view_name.streq(view_table_alias->table_name) &&
|
||||
precedent->view_db.streq(view_table_alias->db))
|
||||
{
|
||||
my_error(ER_VIEW_RECURSIVE, MYF(0),
|
||||
top_view->view_db.str, top_view->view_name.str);
|
||||
@@ -1400,45 +1402,45 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
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).
|
||||
*/
|
||||
old_lex= thd->lex;
|
||||
parent_query_lex= thd->lex;
|
||||
arena= thd->activate_stmt_arena_if_needed(&backup);
|
||||
|
||||
/* init timestamp */
|
||||
if (!table->hr_timestamp.str)
|
||||
table->hr_timestamp.str= table->timestamp_buffer;
|
||||
if (!view_table_alias->hr_timestamp.str)
|
||||
view_table_alias->hr_timestamp.str= view_table_alias->timestamp_buffer;
|
||||
/* prepare default values for old format */
|
||||
table->view_suid= TRUE;
|
||||
table->definer.user.str= table->definer.host.str= 0;
|
||||
table->definer.user.length= table->definer.host.length= 0;
|
||||
view_table_alias->view_suid= TRUE;
|
||||
view_table_alias->definer.user.str= view_table_alias->definer.host.str= 0;
|
||||
view_table_alias->definer.user.length= view_table_alias->definer.host.length= 0;
|
||||
|
||||
/*
|
||||
TODO: when VIEWs will be stored in cache (not only parser),
|
||||
table mem_root should be used here
|
||||
*/
|
||||
DBUG_ASSERT(share->view_def != NULL);
|
||||
if ((result= share->view_def->parse((uchar*)table, thd->mem_root,
|
||||
if ((result= share->view_def->parse((uchar*)view_table_alias, thd->mem_root,
|
||||
view_parameters,
|
||||
required_view_parameters,
|
||||
&file_parser_dummy_hook)))
|
||||
goto end;
|
||||
DBUG_ASSERT(share->tabledef_version.length);
|
||||
if (!table->tabledef_version.length)
|
||||
if (!view_table_alias->tabledef_version.length)
|
||||
{
|
||||
table->set_view_def_version(&table->hr_timestamp);
|
||||
view_table_alias->set_view_def_version(&view_table_alias->hr_timestamp);
|
||||
}
|
||||
|
||||
/*
|
||||
check old format view .frm
|
||||
*/
|
||||
if (!table->definer.user.str)
|
||||
if (!view_table_alias->definer.user.str)
|
||||
{
|
||||
DBUG_ASSERT(!table->definer.host.str &&
|
||||
!table->definer.user.length &&
|
||||
!table->definer.host.length);
|
||||
DBUG_ASSERT(!view_table_alias->definer.host.str &&
|
||||
!view_table_alias->definer.user.length &&
|
||||
!view_table_alias->definer.host.length);
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_VIEW_FRM_NO_USER, ER_THD(thd, ER_VIEW_FRM_NO_USER),
|
||||
table->db.str, table->table_name.str);
|
||||
get_default_definer(thd, &table->definer, false);
|
||||
view_table_alias->db.str, view_table_alias->table_name.str);
|
||||
get_default_definer(thd, &view_table_alias->definer, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1446,15 +1448,15 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
always "" for a Role. Before 10.0.5 it could be "" for a User,
|
||||
but roles didn't exist. file_version helps.
|
||||
*/
|
||||
if (!table->definer.host.str[0] && table->file_version < 2)
|
||||
table->definer.host= host_not_specified; // User, not Role
|
||||
if (!view_table_alias->definer.host.str[0] && view_table_alias->file_version < 2)
|
||||
view_table_alias->definer.host= host_not_specified; // User, not Role
|
||||
|
||||
/*
|
||||
Initialize view definition context by character set names loaded from
|
||||
the view definition file. Use UTF8 character set if view definition
|
||||
file is of old version and does not contain the character set names.
|
||||
*/
|
||||
table->view_creation_ctx= View_creation_ctx::create(thd, table);
|
||||
view_table_alias->view_creation_ctx= View_creation_ctx::create(thd, view_table_alias);
|
||||
|
||||
if (open_view_no_parse)
|
||||
{
|
||||
@@ -1467,8 +1469,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
Save VIEW parameters, which will be wiped out by derived table
|
||||
processing
|
||||
*/
|
||||
table->view_db= table->db;
|
||||
table->view_name= table->table_name;
|
||||
view_table_alias->view_db= view_table_alias->db;
|
||||
view_table_alias->view_name= view_table_alias->table_name;
|
||||
/*
|
||||
We don't invalidate a prepared statement when a view changes,
|
||||
or when someone creates a temporary table.
|
||||
@@ -1482,12 +1484,12 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
but will invoke open_table_from_share(), which will
|
||||
eventually call this function.
|
||||
*/
|
||||
table->open_type= OT_BASE_ONLY;
|
||||
view_table_alias->open_type= OT_BASE_ONLY;
|
||||
|
||||
/*
|
||||
Clear old variables in the TABLE_LIST that could be left from an old view
|
||||
*/
|
||||
table->merged_for_insert= FALSE;
|
||||
view_table_alias->merged_for_insert= FALSE;
|
||||
|
||||
/*TODO: md5 test here and warning if it is differ */
|
||||
|
||||
@@ -1499,8 +1501,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
now Lex placed in statement memory
|
||||
*/
|
||||
|
||||
table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
|
||||
if (!table->view)
|
||||
view_table_alias->view= view_query_lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
|
||||
if (!view_table_alias->view)
|
||||
{
|
||||
result= true;
|
||||
goto end;
|
||||
@@ -1511,41 +1513,41 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
LEX_CSTRING old_db= { old_db_buf, sizeof(old_db_buf) };
|
||||
bool dbchanged;
|
||||
Parser_state parser_state;
|
||||
if (parser_state.init(thd, table->select_stmt.str,
|
||||
(uint)table->select_stmt.length))
|
||||
if (parser_state.init(thd, view_table_alias->select_stmt.str,
|
||||
(uint)view_table_alias->select_stmt.length))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
Use view db name as thread default database, in order to ensure
|
||||
that the view is parsed and prepared correctly.
|
||||
*/
|
||||
if ((result= mysql_opt_change_db(thd, &table->view_db,
|
||||
if ((result= mysql_opt_change_db(thd, &view_table_alias->view_db,
|
||||
(LEX_STRING*) &old_db, 1,
|
||||
&dbchanged)))
|
||||
goto end;
|
||||
|
||||
lex_start(thd);
|
||||
lex->stmt_lex= old_lex;
|
||||
view_query_lex->stmt_lex= parent_query_lex;
|
||||
|
||||
Sql_mode_save_for_frm_handling sql_mode_save(thd);
|
||||
/* Parse the query. */
|
||||
|
||||
parse_status= parse_sql(thd, & parser_state, table->view_creation_ctx);
|
||||
parse_status= parse_sql(thd, & parser_state, view_table_alias->view_creation_ctx);
|
||||
|
||||
view_select= lex->first_select_lex();
|
||||
view_select= view_query_lex->first_select_lex();
|
||||
|
||||
/* Restore environment. */
|
||||
|
||||
if ((old_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
|
||||
(old_lex->sql_command == SQLCOM_SHOW_CREATE))
|
||||
lex->sql_command= old_lex->sql_command;
|
||||
if ((parent_query_lex->sql_command == SQLCOM_SHOW_FIELDS) ||
|
||||
(parent_query_lex->sql_command == SQLCOM_SHOW_CREATE))
|
||||
view_query_lex->sql_command= parent_query_lex->sql_command;
|
||||
|
||||
if (dbchanged && mysql_change_db(thd, &old_db, TRUE))
|
||||
goto err;
|
||||
}
|
||||
if (!parse_status)
|
||||
{
|
||||
TABLE_LIST *view_tables= lex->query_tables;
|
||||
TABLE_LIST *view_tables= view_query_lex->query_tables;
|
||||
TABLE_LIST *view_tables_tail= 0;
|
||||
TABLE_LIST *tbl;
|
||||
Security_context *security_ctx= 0;
|
||||
@@ -1556,16 +1558,16 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
cases when the current user does not have rights for the
|
||||
underlying tables.
|
||||
*/
|
||||
if (!table->prelocking_placeholder)
|
||||
opt_trace_disable_if_no_view_access(thd, table, view_tables);
|
||||
if (!view_table_alias->prelocking_placeholder)
|
||||
opt_trace_disable_if_no_view_access(thd, view_table_alias, view_tables);
|
||||
|
||||
/*
|
||||
Check rights to run commands (ANALYZE SELECT, EXPLAIN SELECT &
|
||||
SHOW CREATE) which show underlying tables.
|
||||
Skip this step if we are opening view for prelocking only.
|
||||
*/
|
||||
if (!table->prelocking_placeholder && (old_lex->describe ||
|
||||
old_lex->analyze_stmt))
|
||||
if (!view_table_alias->prelocking_placeholder && (parent_query_lex->describe ||
|
||||
parent_query_lex->analyze_stmt))
|
||||
{
|
||||
/*
|
||||
The user we run EXPLAIN as (either the connected user who issued
|
||||
@@ -1590,11 +1592,11 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
Finally at this point making sure we have SHOW_VIEW_ACL on the views
|
||||
will suffice as we implicitly require SELECT_ACL anyway.
|
||||
*/
|
||||
|
||||
|
||||
TABLE_LIST view_no_suid;
|
||||
bzero(static_cast<void *>(&view_no_suid), sizeof(TABLE_LIST));
|
||||
view_no_suid.db= table->db;
|
||||
view_no_suid.table_name= table->table_name;
|
||||
view_no_suid.db= view_table_alias->db;
|
||||
view_no_suid.table_name= view_table_alias->table_name;
|
||||
|
||||
DBUG_ASSERT(view_tables == NULL || view_tables->security_ctx == NULL);
|
||||
|
||||
@@ -1608,15 +1610,15 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else if (!table->prelocking_placeholder &&
|
||||
(old_lex->sql_command == SQLCOM_SHOW_CREATE) &&
|
||||
!table->belong_to_view)
|
||||
else if (!view_table_alias->prelocking_placeholder &&
|
||||
(parent_query_lex->sql_command == SQLCOM_SHOW_CREATE) &&
|
||||
!view_table_alias->belong_to_view)
|
||||
{
|
||||
if (check_table_access(thd, SHOW_VIEW_ACL, table, FALSE, UINT_MAX, FALSE))
|
||||
if (check_table_access(thd, SHOW_VIEW_ACL, view_table_alias, FALSE, UINT_MAX, FALSE))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(table->view_tables=
|
||||
if (!(view_table_alias->view_tables=
|
||||
(List<TABLE_LIST>*) new(thd->mem_root) List<TABLE_LIST>))
|
||||
goto err;
|
||||
/*
|
||||
@@ -1628,9 +1630,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
tbl= (view_tables_tail= tbl)->next_global)
|
||||
{
|
||||
tbl->open_type= OT_BASE_ONLY;
|
||||
tbl->belong_to_view= top_view;
|
||||
tbl->referencing_view= table;
|
||||
tbl->prelocking_placeholder= table->prelocking_placeholder;
|
||||
tbl->belong_to_view= top_view; // alias of VIEW referred to by the parent query
|
||||
tbl->referencing_view= view_table_alias;
|
||||
tbl->prelocking_placeholder= view_table_alias->prelocking_placeholder;
|
||||
/*
|
||||
First we fill want_privilege with SELECT_ACL (this is needed for the
|
||||
tables which belongs to view subqueries and temporary table views,
|
||||
@@ -1646,7 +1648,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
subqueries used in view definition).
|
||||
Let's build a list of all tables referenced in the view.
|
||||
*/
|
||||
table->view_tables->push_back(tbl);
|
||||
view_table_alias->view_tables->push_back(tbl);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1658,41 +1660,41 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
*/
|
||||
if (view_tables)
|
||||
{
|
||||
if (table->next_global)
|
||||
if (view_table_alias->next_global)
|
||||
{
|
||||
view_tables_tail->next_global= table->next_global;
|
||||
table->next_global->prev_global= &view_tables_tail->next_global;
|
||||
view_tables_tail->next_global= view_table_alias->next_global;
|
||||
view_table_alias->next_global->prev_global= &view_tables_tail->next_global;
|
||||
}
|
||||
else
|
||||
{
|
||||
old_lex->query_tables_last= &view_tables_tail->next_global;
|
||||
parent_query_lex->query_tables_last= &view_tables_tail->next_global;
|
||||
}
|
||||
view_tables->prev_global= &table->next_global;
|
||||
table->next_global= view_tables;
|
||||
view_tables->prev_global= &view_table_alias->next_global;
|
||||
view_table_alias->next_global= view_tables;
|
||||
}
|
||||
|
||||
/*
|
||||
If the view's body needs row-based binlogging (e.g. the VIEW is created
|
||||
from SELECT UUID()), the top statement also needs it.
|
||||
*/
|
||||
old_lex->set_stmt_unsafe_flags(lex->get_stmt_unsafe_flags());
|
||||
parent_query_lex->set_stmt_unsafe_flags(view_query_lex->get_stmt_unsafe_flags());
|
||||
|
||||
view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
lex->can_be_merged());
|
||||
view_is_mergeable= (view_table_alias->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
view_query_lex->can_be_merged());
|
||||
|
||||
if (view_is_mergeable)
|
||||
{
|
||||
/*
|
||||
Currently 'view_main_select_tables' differs from 'view_tables'
|
||||
only then view has CONVERT_TZ() function in its select list.
|
||||
only when view has CONVERT_TZ() function in its select list.
|
||||
This may change in future, for example if we enable merging of
|
||||
views with subqueries in select list.
|
||||
*/
|
||||
view_main_select_tables= lex->first_select_lex()->table_list.first;
|
||||
view_main_select_tables= view_query_lex->first_select_lex()->table_list.first;
|
||||
/*
|
||||
Mergeable view can be used for inserting, so we move the flag down
|
||||
*/
|
||||
if (table->for_insert_data)
|
||||
if (view_table_alias->for_insert_data)
|
||||
{
|
||||
for (TABLE_LIST *t= view_main_select_tables;
|
||||
t;
|
||||
@@ -1713,9 +1715,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
{
|
||||
/* We have to keep the lock type for sequence tables */
|
||||
if (!tbl->sequence)
|
||||
tbl->lock_type= table->lock_type;
|
||||
tbl->mdl_request.set_type(table->mdl_request.type);
|
||||
tbl->updating= table->updating;
|
||||
tbl->lock_type= view_table_alias->lock_type;
|
||||
tbl->mdl_request.set_type(view_table_alias->mdl_request.type);
|
||||
tbl->updating= view_table_alias->updating;
|
||||
}
|
||||
/*
|
||||
If the view is mergeable, we might want to
|
||||
@@ -1723,47 +1725,47 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
original sql command and 'duplicates' of the outer lex.
|
||||
This is used later in set_trg_event_type_for_command.
|
||||
*/
|
||||
lex->sql_command= old_lex->sql_command;
|
||||
lex->duplicates= old_lex->duplicates;
|
||||
view_query_lex->sql_command= parent_query_lex->sql_command;
|
||||
view_query_lex->duplicates= parent_query_lex->duplicates;
|
||||
|
||||
/* Fields in this view can be used in upper select in case of merge. */
|
||||
if (table->select_lex)
|
||||
table->select_lex->add_where_field(lex->first_select_lex());
|
||||
if (view_table_alias->select_lex)
|
||||
view_table_alias->select_lex->add_where_field(view_query_lex->first_select_lex());
|
||||
}
|
||||
/*
|
||||
This method has a dependency on the proper lock type being set,
|
||||
so in case of views should be called here.
|
||||
*/
|
||||
lex->set_trg_event_type_for_tables();
|
||||
view_query_lex->set_trg_event_type_for_tables();
|
||||
|
||||
/*
|
||||
If we are opening this view as part of implicit LOCK TABLES, then
|
||||
this view serves as simple placeholder and we should not continue
|
||||
further processing.
|
||||
*/
|
||||
if (table->prelocking_placeholder)
|
||||
if (view_table_alias->prelocking_placeholder)
|
||||
goto ok2;
|
||||
|
||||
old_lex->derived_tables|= (DERIVED_VIEW | lex->derived_tables);
|
||||
parent_query_lex->derived_tables|= (DERIVED_VIEW | view_query_lex->derived_tables);
|
||||
|
||||
/* 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);
|
||||
parent_query_lex->safe_to_cache_query= (parent_query_lex->safe_to_cache_query &&
|
||||
view_query_lex->safe_to_cache_query);
|
||||
/* move SQL_CACHE to whole query */
|
||||
if (lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE)
|
||||
old_lex->first_select_lex()->options|= OPTION_TO_QUERY_CACHE;
|
||||
if (view_query_lex->first_select_lex()->options & OPTION_TO_QUERY_CACHE)
|
||||
parent_query_lex->first_select_lex()->options|= OPTION_TO_QUERY_CACHE;
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (table->view_suid)
|
||||
if (view_table_alias->view_suid)
|
||||
{
|
||||
/*
|
||||
For suid views prepare a security context for checking underlying
|
||||
objects of the view.
|
||||
*/
|
||||
if (!(table->view_sctx=
|
||||
if (!(view_table_alias->view_sctx=
|
||||
thd->active_stmt_arena_to_use()->calloc<Security_context>(1)))
|
||||
goto err;
|
||||
security_ctx= table->view_sctx;
|
||||
security_ctx= view_table_alias->view_sctx;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1772,7 +1774,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
This allows properly handle situation when non-suid view is used
|
||||
from within suid view.
|
||||
*/
|
||||
security_ctx= table->security_ctx;
|
||||
security_ctx= view_table_alias->security_ctx;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1786,7 +1788,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
}
|
||||
|
||||
/* assign security context to SELECT name resolution contexts of view */
|
||||
for(SELECT_LEX *sl= lex->all_selects_list;
|
||||
for(SELECT_LEX *sl= view_query_lex->all_selects_list;
|
||||
sl;
|
||||
sl= sl->next_select_in_list())
|
||||
sl->context.security_ctx= security_ctx;
|
||||
@@ -1795,12 +1797,12 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
Setup an error processor to hide error messages issued by stored
|
||||
routines referenced in the view
|
||||
*/
|
||||
for (SELECT_LEX *sl= lex->all_selects_list;
|
||||
for (SELECT_LEX *sl= view_query_lex->all_selects_list;
|
||||
sl;
|
||||
sl= sl->next_select_in_list())
|
||||
{
|
||||
sl->context.error_processor= &view_error_processor;
|
||||
sl->context.error_processor_data= (void *)table;
|
||||
sl->context.error_processor_data= (void *)view_table_alias;
|
||||
}
|
||||
|
||||
view_select->master_unit()->is_view= true;
|
||||
@@ -1812,57 +1814,57 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
- VIEW used in subquery or command support MERGE algorithm
|
||||
*/
|
||||
if (view_is_mergeable &&
|
||||
(table->select_lex->master_unit() != &old_lex->unit ||
|
||||
old_lex->can_use_merged()) &&
|
||||
!old_lex->can_not_use_merged())
|
||||
(view_table_alias->select_lex->master_unit() != &parent_query_lex->unit ||
|
||||
parent_query_lex->can_use_merged()) &&
|
||||
!parent_query_lex->can_not_use_merged())
|
||||
{
|
||||
/* lex should contain at least one table */
|
||||
DBUG_ASSERT(view_main_select_tables != 0);
|
||||
|
||||
List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
|
||||
|
||||
table->derived_type= VIEW_ALGORITHM_MERGE;
|
||||
view_table_alias->derived_type= VIEW_ALGORITHM_MERGE;
|
||||
DBUG_PRINT("info", ("algorithm: MERGE"));
|
||||
table->updatable= (table->updatable_view != 0);
|
||||
table->effective_with_check=
|
||||
old_lex->get_effective_with_check(table);
|
||||
table->merge_underlying_list= view_main_select_tables;
|
||||
view_table_alias->updatable= (view_table_alias->updatable_view != 0);
|
||||
view_table_alias->effective_with_check=
|
||||
parent_query_lex->get_effective_with_check(view_table_alias);
|
||||
view_table_alias->merge_underlying_list= view_main_select_tables;
|
||||
|
||||
/* Fill correct wanted privileges. */
|
||||
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
|
||||
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
|
||||
|
||||
/* prepare view context */
|
||||
lex->first_select_lex()->
|
||||
view_query_lex->first_select_lex()->
|
||||
context.resolve_in_table_list_only(view_main_select_tables);
|
||||
lex->first_select_lex()->context.outer_context= 0;
|
||||
lex->first_select_lex()->select_n_having_items+=
|
||||
table->select_lex->select_n_having_items;
|
||||
view_query_lex->first_select_lex()->context.outer_context= 0;
|
||||
view_query_lex->first_select_lex()->select_n_having_items+=
|
||||
view_table_alias->select_lex->select_n_having_items;
|
||||
|
||||
table->where= view_select->where;
|
||||
view_table_alias->where= view_select->where;
|
||||
|
||||
/*
|
||||
We can safely ignore the VIEW's ORDER BY if we merge into union
|
||||
branch, as order is not important there.
|
||||
*/
|
||||
if (!table->select_lex->master_unit()->is_unit_op() &&
|
||||
table->select_lex->order_list.elements == 0)
|
||||
if (!view_table_alias->select_lex->master_unit()->is_unit_op() &&
|
||||
view_table_alias->select_lex->order_list.elements == 0)
|
||||
{
|
||||
table->select_lex->order_list.
|
||||
push_back(&lex->first_select_lex()->order_list);
|
||||
lex->first_select_lex()->order_list.empty();
|
||||
view_table_alias->select_lex->order_list.
|
||||
push_back(&view_query_lex->first_select_lex()->order_list);
|
||||
view_query_lex->first_select_lex()->order_list.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (old_lex->sql_command == SQLCOM_SELECT &&
|
||||
(old_lex->describe & DESCRIBE_EXTENDED) &&
|
||||
lex->first_select_lex()->order_list.elements &&
|
||||
!table->select_lex->master_unit()->is_unit_op())
|
||||
if (parent_query_lex->sql_command == SQLCOM_SELECT &&
|
||||
(parent_query_lex->describe & DESCRIBE_EXTENDED) &&
|
||||
view_query_lex->first_select_lex()->order_list.elements &&
|
||||
!view_table_alias->select_lex->master_unit()->is_unit_op())
|
||||
{
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_VIEW_ORDERBY_IGNORED,
|
||||
ER_THD(thd, ER_VIEW_ORDERBY_IGNORED),
|
||||
table->db.str, table->table_name.str);
|
||||
view_table_alias->db.str, view_table_alias->table_name.str);
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -1874,51 +1876,51 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
||||
goto ok;
|
||||
}
|
||||
|
||||
table->derived_type= VIEW_ALGORITHM_TMPTABLE;
|
||||
view_table_alias->derived_type= VIEW_ALGORITHM_TMPTABLE;
|
||||
DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
|
||||
view_select->linkage= DERIVED_TABLE_TYPE;
|
||||
table->updatable= 0;
|
||||
table->effective_with_check= VIEW_CHECK_NONE;
|
||||
view_table_alias->updatable= 0;
|
||||
view_table_alias->effective_with_check= VIEW_CHECK_NONE;
|
||||
|
||||
table->derived= &lex->unit;
|
||||
view_table_alias->derived= &view_query_lex->unit;
|
||||
}
|
||||
else
|
||||
goto err;
|
||||
|
||||
ok:
|
||||
/* SELECT tree link */
|
||||
lex->unit.include_down(table->select_lex);
|
||||
lex->unit.slave= view_select; // fix include_down initialisation
|
||||
view_query_lex->unit.include_down(view_table_alias->select_lex);
|
||||
view_query_lex->unit.slave= view_select; // fix include_down initialisation
|
||||
/* global SELECT list linking */
|
||||
/*
|
||||
The primary SELECT_LEX is always last (because parsed first) if WITH not
|
||||
used, otherwise it is good start point for last element finding
|
||||
*/
|
||||
for (end= view_select; end->link_next; end= end->link_next);
|
||||
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;
|
||||
end->link_next= parent_query_lex->all_selects_list;
|
||||
parent_query_lex->all_selects_list->link_prev= &end->link_next;
|
||||
parent_query_lex->all_selects_list= view_query_lex->all_selects_list;
|
||||
view_query_lex->all_selects_list->link_prev=
|
||||
(st_select_lex_node**)&parent_query_lex->all_selects_list;
|
||||
|
||||
ok2:
|
||||
DBUG_ASSERT(lex == thd->lex);
|
||||
thd->lex= old_lex; // Needed for prepare_security
|
||||
result= !table->prelocking_placeholder && table->prepare_security(thd);
|
||||
DBUG_ASSERT(view_query_lex == thd->lex);
|
||||
thd->lex= parent_query_lex; // Needed for prepare_security
|
||||
result= !view_table_alias->prelocking_placeholder && view_table_alias->prepare_security(thd);
|
||||
|
||||
lex_end(lex);
|
||||
lex_end(view_query_lex);
|
||||
end:
|
||||
if (arena)
|
||||
thd->restore_active_arena(arena, &backup);
|
||||
thd->lex= old_lex;
|
||||
thd->lex= parent_query_lex;
|
||||
status_var_increment(thd->status_var.opened_views);
|
||||
DBUG_RETURN(result);
|
||||
|
||||
err:
|
||||
DBUG_ASSERT(thd->lex == table->view);
|
||||
DBUG_ASSERT(thd->lex == view_table_alias->view);
|
||||
lex_end(thd->lex);
|
||||
delete table->view;
|
||||
table->view= 0; // now it is not VIEW placeholder
|
||||
delete view_table_alias->view;
|
||||
view_table_alias->view= 0; // now it is not VIEW placeholder
|
||||
result= 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user