diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 374 |
1 files changed, 238 insertions, 136 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 079d8994549..55e0d33cdde 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -76,7 +76,7 @@ const char *any_db="*any*"; // Special symbol for check_access const char *command_name[]={ "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB", "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", - "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", + "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user", "Binlog Dump","Table Dump", "Connect Out", "Register Slave", "Prepare", "Prepare Execute", "Long Data", "Close stmt", "Reset stmt", "Set option", "Fetch", @@ -516,12 +516,17 @@ void free_max_user_conn(void) /* Mark all commands that somehow changes a table This is used to check number of updates / hour + + sql_command is actually set to SQLCOM_END sometimes + so we need the +1 to include it in the array. */ -char uc_update_queries[SQLCOM_END]; +char uc_update_queries[SQLCOM_END+1]; void init_update_queries(void) { + bzero((gptr) &uc_update_queries, sizeof(uc_update_queries)); + uc_update_queries[SQLCOM_CREATE_TABLE]=1; uc_update_queries[SQLCOM_CREATE_INDEX]=1; uc_update_queries[SQLCOM_ALTER_TABLE]=1; @@ -548,6 +553,7 @@ void init_update_queries(void) bool is_update_query(enum enum_sql_command command) { + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); return uc_update_queries[command]; } @@ -912,7 +918,7 @@ static int check_connection(THD *thd) x_free(thd->user); if (!(thd->user= my_strdup(user, MYF(0)))) return (ER_OUT_OF_RESOURCES); - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); } @@ -1019,6 +1025,10 @@ pthread_handler_decl(handle_one_connection,arg) net->compress=1; // Use compression thd->version= refresh_version; + thd->proc_info= 0; + thd->set_time(); + thd->init_for_queries(); + if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); @@ -1039,12 +1049,12 @@ pthread_handler_decl(handle_one_connection,arg) if (net->error && net->vio != 0 && net->report_error) { if (!thd->killed && thd->variables.log_warnings > 1) - sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - thd->user ? thd->user : "unauthenticated", - thd->host_or_ip, - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user ? thd->user : "unauthenticated", + thd->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); send_error(thd,net->last_errno,NullS); statistic_increment(aborted_threads,&LOCK_status); } @@ -1599,6 +1609,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CREATE_DB: // QQ: To be removed { char *db=thd->strdup(packet), *alias; + HA_CREATE_INFO create_info; statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB], &LOCK_status); @@ -1611,7 +1622,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); - mysql_create_db(thd,(lower_case_table_names == 2 ? alias : db),0,0); + bzero(&create_info, sizeof(create_info)); + if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + &create_info, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } case COM_DROP_DB: // QQ: To be removed @@ -1633,7 +1647,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } mysql_log.write(thd,command,db); - mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), 0, 0); + if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), + 0, 0) < 0) + send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); break; } #ifndef EMBEDDED_LIBRARY @@ -1904,12 +1920,12 @@ mysql_execute_command(THD *thd) { int res= 0; LEX *lex= thd->lex; + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &lex->select_lex; /* first table of first SELECT_LEX */ - TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; /* list of all tables in query */ TABLE_LIST *all_tables; - /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ - SELECT_LEX *select_lex= &lex->select_lex; /* most outer SELECT_LEX_UNIT of query */ SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_execute_command"); @@ -2084,6 +2100,7 @@ mysql_execute_command(THD *thd) CHARSET_INFO *to_cs= thd->variables.collation_connection; bool need_conversion; user_var_entry *entry; + String *pstr= &str; uint32 unused; /* Convert @var contents to string in connection character set. Although @@ -2096,29 +2113,45 @@ mysql_execute_command(THD *thd) lex->prepared_stmt_code.length)) && entry->value) { - String *pstr; my_bool is_var_null; pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC); + /* + NULL value of variable checked early as entry->value so here + we can't get NULL in normal conditions + */ DBUG_ASSERT(!is_var_null); if (!pstr) - send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_ASSERT(pstr == &str); + { + res= -1; + break; // EOM (error should be reported by allocator) + } } else + { + /* + variable absent or equal to NULL, so we need to set variable to + something reasonable to get readable error message during parsing + */ str.set("NULL", 4, &my_charset_latin1); + } + need_conversion= - String::needs_conversion(str.length(), str.charset(), to_cs, &unused); + String::needs_conversion(pstr->length(), pstr->charset(), + to_cs, &unused); - query_len= need_conversion? (str.length() * to_cs->mbmaxlen) : - str.length(); + query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) : + pstr->length(); if (!(query_str= alloc_root(&thd->mem_root, query_len+1))) - send_error(thd, ER_OUT_OF_RESOURCES); + { + res= -1; + break; // EOM (error should be reported by allocator) + } if (need_conversion) - query_len= copy_and_convert(query_str, query_len, to_cs, str.ptr(), - str.length(), str.charset()); + query_len= copy_and_convert(query_str, query_len, to_cs, pstr->ptr(), + pstr->length(), pstr->charset()); else - memcpy(query_str, str.ptr(), str.length()); + memcpy(query_str, pstr->ptr(), pstr->length()); query_str[query_len]= 0; } else @@ -2414,9 +2447,6 @@ mysql_execute_command(THD *thd) { select_result *result; - if (select_tables && - check_table_access(thd, SELECT_ACL, select_tables, 0)) - goto create_error; // Error message is given select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex, select_lex); @@ -2552,7 +2582,7 @@ unsent_create_error: if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { net_printf(thd, ER_WRONG_TABLE_NAME, lex->name); - res=0; + res= 1; break; } if (!select_lex->db) @@ -2775,12 +2805,11 @@ unsent_create_error: case SQLCOM_INSERT: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0); - if ((res= insert_precheck(thd, all_tables, update))) + if ((res= insert_precheck(thd, all_tables))) break; res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, select_lex->item_list, lex->value_list, - (update ? DUP_UPDATE : lex->duplicates)); + lex->duplicates); if (thd->net.report_error) res= -1; if (first_table->view && !first_table->contain_auto_increment) @@ -2895,10 +2924,8 @@ unsent_create_error: } thd->proc_info="init"; - if ((res= open_and_lock_tables(thd, all_tables))) - break; - - if ((res= mysql_multi_delete_prepare(thd))) + if ((res= open_and_lock_tables(thd, all_tables)) || + (res= mysql_multi_delete_prepare(thd))) break; if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, @@ -4134,6 +4161,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, thd->priv_user, db, test(want_access & GRANT_ACL)); else db_access=thd->db_access; + DBUG_PRINT("info",("db_access: %lu", db_access)); /* Remove SHOW attribute and access rights we already have */ want_access &= ~(thd->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", @@ -4206,7 +4234,10 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, TABLE_LIST *org_tables=tables; for (; tables; tables= tables->next_global) { - if (tables->derived || (tables->table && (int)tables->table->tmp_table)) + if (tables->derived || + (tables->table && (int)tables->table->tmp_table) || + my_tz_check_n_skip_implicit_tables(&tables, + thd->lex->time_zone_tables_used)) continue; if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) && thd->db) @@ -4411,58 +4442,44 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) ****************************************************************************/ void -mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly) +mysql_init_query(THD *thd, uchar *buf, uint length) { DBUG_ENTER("mysql_init_query"); - LEX *lex= thd->lex; - lex->unit.init_query(); - lex->unit.init_select(); - lex->unit.thd= thd; - lex->select_lex.init_query(); - lex->value_list.empty(); - lex->param_list.empty(); - lex->view_list.empty(); - lex->unit.next= lex->unit.master= - lex->unit.link_next= lex->unit.return_to=0; - lex->unit.prev= lex->unit.link_prev= 0; - lex->unit.slave= lex->unit.global_parameters= lex->current_select= - lex->all_selects_list= &lex->select_lex; - lex->select_lex.master= &lex->unit; - lex->select_lex.prev= &lex->unit.slave; - lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; - lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); - lex->select_lex.options=0; - lex->select_lex.init_order(); - lex->select_lex.group_list.empty(); - lex->describe= 0; - lex->derived_tables= 0; - lex->view_prepare_mode= FALSE; - lex->lock_option= TL_READ; - lex->found_colon= 0; - lex->safe_to_cache_query= 1; - lex->time_zone_tables_used= 0; - lex->proc_table= lex->query_tables= 0; - lex->query_tables_last= &lex->query_tables; - lex->variables_used= 0; - lex->select_lex.parent_lex= lex; - lex->empty_field_list_on_rset= 0; lex_start(thd, buf, length); - if (! lexonly) - { - thd->select_number= lex->select_lex.select_number= 1; - thd->free_list= 0; - thd->total_warn_count=0; // Warnings for this query - thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; - thd->sent_row_count= thd->examined_row_count= 0; - thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; - thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | - SERVER_QUERY_NO_INDEX_USED | - SERVER_QUERY_NO_GOOD_INDEX_USED); - thd->tmp_table_used= 0; - if (opt_bin_log) - reset_dynamic(&thd->user_var_events); - thd->clear_error(); - } + mysql_reset_thd_for_next_command(thd); + DBUG_VOID_RETURN; +} + + +/* + Reset THD part responsible for command processing state. + + DESCRIPTION + This needs to be called before execution of every statement + (prepared or conventional). + + TODO + Make it a method of THD and align its name with the rest of + reset/end/start/init methods. + Call it after we use THD for queries, not before. +*/ + +void mysql_reset_thd_for_next_command(THD *thd) +{ + DBUG_ENTER("mysql_reset_thd_for_next_command"); + thd->free_list= 0; + thd->select_number= 1; + thd->total_warn_count=0; // Warnings for this query + thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->sent_row_count= thd->examined_row_count= 0; + thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; + thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | + SERVER_QUERY_NO_INDEX_USED | + SERVER_QUERY_NO_GOOD_INDEX_USED); + thd->tmp_table_used= 0; + if (opt_bin_log) + reset_dynamic(&thd->user_var_events); + thd->clear_error(); DBUG_VOID_RETURN; } @@ -4647,6 +4664,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) thd->proc_info="freeing items"; thd->end_statement(); thd->cleanup_after_query(); + DBUG_ASSERT(thd->change_list.is_empty()); } DBUG_VOID_RETURN; } @@ -4679,6 +4697,32 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) #endif + +/* + Calculate interval lengths. + Strip trailing spaces from all strings. + After this function call: + - ENUM uses max_length + - SET uses tot_length. +*/ +void calculate_interval_lengths(THD *thd, TYPELIB *interval, + uint *max_length, uint *tot_length) +{ + const char **pos; + uint *len; + CHARSET_INFO *cs= thd->variables.character_set_client; + *max_length= *tot_length= 0; + for (pos= interval->type_names, len= interval->type_lengths; + *pos ; pos++, len++) + { + *len= (uint) strip_sp((char*) *pos); + uint length= cs->cset->numchars(cs, *pos, *pos + *len); + *tot_length+= length; + set_if_bigger(*max_length, length); + } +} + + /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -4737,7 +4781,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } else if (default_value->type() == Item::NULL_ITEM) { - default_value=0; + default_value= 0; if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { @@ -4808,23 +4852,23 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, switch (type) { case FIELD_TYPE_TINY: - if (!length) new_field->length=3+sign_len; + if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_SHORT: - if (!length) new_field->length=5+sign_len; + if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_INT24: - if (!length) new_field->length=8+sign_len; + if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_LONG: - if (!length) new_field->length=10+sign_len; + if (!length) new_field->length=MAX_INT_WIDTH+sign_len; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_LONGLONG: - if (!length) new_field->length=20; + if (!length) new_field->length=MAX_BIGINT_WIDTH; allowed_type_modifier= AUTO_INCREMENT_FLAG; break; case FIELD_TYPE_NULL: @@ -4939,7 +4983,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */ new_field->length= min(new_field->length,14); /* purecov: inspected */ } - new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG; + new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; if (default_value) { /* Grammar allows only NOW() value for ON UPDATE clause */ @@ -4960,13 +5004,24 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } else { - /* - We are setting TIMESTAMP_OLD_FIELD here only temporary, we will - replace this value by TIMESTAMP_DNUN_FIELD or NONE later when - information about all TIMESTAMP fields in table will be available. + /* + If we have default TIMESTAMP NOT NULL column without explicit DEFAULT + or ON UPDATE values then for the sake of compatiblity we should treat + this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't + have another TIMESTAMP column with auto-set option before this one) + or DEFAULT 0 (in other cases). + So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will + replace this value by TIMESTAMP_DNUN_FIELD or NONE later when + information about all TIMESTAMP fields in table will be availiable. + + If we have TIMESTAMP NULL column without explicit DEFAULT value + we treat it as having DEFAULT NULL attribute. */ - new_field->unireg_check= on_update_value?Field::TIMESTAMP_UN_FIELD: - Field::TIMESTAMP_OLD_FIELD; + new_field->unireg_check= (on_update_value ? + Field::TIMESTAMP_UN_FIELD : + (new_field->flags & NOT_NULL_FLAG ? + Field::TIMESTAMP_OLD_FIELD: + Field::NONE)); } break; case FIELD_TYPE_DATE: // Old date type @@ -4993,15 +5048,10 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if (new_field->pack_length > 4) new_field->pack_length=8; new_field->interval=interval; - new_field->length=0; - for (const char **pos=interval->type_names; *pos ; pos++) - { - uint length= (uint) strip_sp((char*) *pos)+1; - CHARSET_INFO *cs= thd->variables.character_set_client; - length= cs->cset->numchars(cs, *pos, *pos+length); - new_field->length+= length; - } - new_field->length--; + uint dummy_max_length; + calculate_interval_lengths(thd, interval, + &dummy_max_length, &new_field->length); + new_field->length+= (interval->count - 1); set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { @@ -5012,8 +5062,9 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, thd->cuted_fields=0; String str,*res; res=default_value->val_str(&str); - (void) find_set(interval, res->ptr(), res->length(), ¬_used, - ¬_used2, ¬_used3); + (void) find_set(interval, res->ptr(), res->length(), + &my_charset_bin, + ¬_used, ¬_used2, ¬_used3); if (thd->cuted_fields) { net_printf(thd,ER_INVALID_DEFAULT,field_name); @@ -5026,14 +5077,10 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { new_field->interval=interval; new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe - new_field->length=(uint) strip_sp((char*) interval->type_names[0]); - for (const char **pos=interval->type_names+1; *pos ; pos++) - { - uint length=(uint) strip_sp((char*) *pos); - CHARSET_INFO *cs= thd->variables.character_set_client; - length= cs->cset->numchars(cs, *pos, *pos+length); - set_if_bigger(new_field->length,length); - } + + uint dummy_tot_length; + calculate_interval_lengths(thd, interval, + &new_field->length, &dummy_tot_length); set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { @@ -5225,6 +5272,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->db= empty_c_string; ptr->db_length= 0; } + if (thd->current_arena->is_stmt_prepare()) + ptr->db= thd->strdup(ptr->db); ptr->alias= alias_str; if (lower_case_table_names && table->table.length) @@ -5601,7 +5650,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, acl_reload(thd); grant_reload(thd); if (mqh_used) - reset_mqh(thd,(LEX_USER *) NULL,true); + reset_mqh(thd,(LEX_USER *) NULL,TRUE); } #endif if (options & REFRESH_LOG) @@ -5990,12 +6039,15 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) */ for (table= tables; table; table= table->next_local) { - if ((check_access(thd, UPDATE_ACL, table->db, - &table->grant.privilege, 0, 1) || - grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && - (check_access(thd, SELECT_ACL, table->db, - &table->grant.privilege, 0, 0) || - grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + if (table->derived) + table->grant.privilege= SELECT_ACL; + else if ((check_access(thd, UPDATE_ACL, table->db, + &table->grant.privilege, 0, 1) || + grant_option && + check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && + (check_access(thd, SELECT_ACL, table->db, + &table->grant.privilege, 0, 0) || + grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) DBUG_RETURN(1); table->table_in_first_from_clause= 1; @@ -6005,9 +6057,10 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) */ if (&lex->select_lex != lex->all_selects_list) { + DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next_global) { - if (!table->table_in_first_from_clause) + if (!table->table_in_first_from_clause && table->derived) { if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0) || @@ -6094,7 +6147,7 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) INSERT ... SELECT query pre-check SYNOPSIS - multi_delete_precheck() + insert_delete_precheck() thd Thread handler tables Global table list @@ -6183,13 +6236,14 @@ int delete_precheck(THD *thd, TABLE_LIST *tables) -1 error (message is not sent to user) */ -int insert_precheck(THD *thd, TABLE_LIST *tables, bool update) +int insert_precheck(THD *thd, TABLE_LIST *tables) { LEX *lex= thd->lex; DBUG_ENTER("insert_precheck"); - ulong privilege= (lex->duplicates == DUP_REPLACE ? - INSERT_ACL | DELETE_ACL : INSERT_ACL | update); + ulong privilege= INSERT_ACL | + (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | + (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0); if (check_one_table_access(thd, privilege, tables)) DBUG_RETURN(1); @@ -6215,26 +6269,74 @@ int insert_precheck(THD *thd, TABLE_LIST *tables, bool update) RETURN VALUE 0 OK 1 Error (message is sent to user) - -1 Error (message is not sent to user) */ int create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table) { LEX *lex= thd->lex; + SELECT_LEX *select_lex= &lex->select_lex; + ulong want_priv; + int error= 1; // Error message is given DBUG_ENTER("create_table_precheck"); - ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? - CREATE_TMP_ACL : CREATE_ACL); + + want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? + CREATE_TMP_ACL : CREATE_ACL); lex->create_info.alias= create_table->alias; if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, 0, 0) || check_merge_table_access(thd, create_table->db, (TABLE_LIST *) lex->create_info.merge_list.first)) - DBUG_RETURN(1); - DBUG_RETURN((grant_option && want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) ? - 1 : 0); + goto err; + if (grant_option && want_priv != CREATE_TMP_ACL && + check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) + goto err; + + if (select_lex->item_list.elements) + { + /* Check permissions for used tables in CREATE TABLE ... SELECT */ + + /* + Only do the check for PS, becasue we on execute we have to check that + against the opened tables to ensure we don't use a table that is part + of the view (which can only be done after the table has been opened). + */ + if (thd->current_arena->is_stmt_prepare()) + { + /* + For temporary tables we don't have to check if the created table exists + */ + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + find_real_table_in_list(tables, create_table->db, + create_table->real_name)) + { + net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name); + + goto err; + } + if (lex->create_info.used_fields & HA_CREATE_USED_UNION) + { + TABLE_LIST *tab; + for (tab= tables; tab; tab= tab->next) + { + if (find_real_table_in_list((TABLE_LIST*) lex->create_info. + merge_list.first, + tables->db, tab->real_name)) + { + net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name); + goto err; + } + } + } + } + if (tables && check_table_access(thd, SELECT_ACL, tables,0)) + goto err; + } + error= 0; + +err: + DBUG_RETURN(error); } |