sql_parse.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:167k
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
- if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
- {
- /* Using same table for INSERT and SELECT */
- select_lex->options |= OPTION_BUFFER_RESULT;
- }
- if ((res= open_and_lock_tables(thd, tables)))
- break;
-
- insert_table= tables->table;
- /* Skip first table, which is the table we are inserting in */
- select_lex->table_list.first= (byte*) first_local_table->next;
- tables= (TABLE_LIST *) select_lex->table_list.first;
- dup_tables= *first_local_table;
- first_local_table->next= 0;
- if (select_lex->group_list.elements != 0)
- {
- /*
- When we are using GROUP BY we can't refere to other tables in the
- ON DUPLICATE KEY part
- */
- dup_tables.next= 0;
- }
- if (!(res= mysql_prepare_insert(thd, tables, first_local_table,
- &dup_tables, insert_table,
- lex->field_list, 0,
- lex->update_list, lex->value_list,
- lex->duplicates)) &&
- (result= new select_insert(insert_table, first_local_table,
- &dup_tables, &lex->field_list,
- &lex->update_list, &lex->value_list,
- lex->duplicates, lex->ignore)))
- {
- /*
- insert/replace from SELECT give its SELECT_LEX for SELECT,
- and item_list belong to SELECT
- */
- lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
- res= handle_select(thd, lex, result);
- /* revert changes for SP */
- lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
- delete result;
- if (thd->net.report_error)
- res= -1;
- }
- else
- res= -1;
- insert_table->insert_values= 0; // Set by mysql_prepare_insert()
- first_local_table->next= tables;
- lex->select_lex.table_list.first= (byte*) first_local_table;
- break;
- }
- case SQLCOM_TRUNCATE:
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- if (check_one_table_access(thd, DELETE_ACL, tables))
- goto error;
- /*
- Don't allow this within a transaction because we want to use
- re-generate table
- */
- if (thd->locked_tables || thd->active_transaction())
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
- goto error;
- }
- res=mysql_truncate(thd, tables, 0);
- break;
- case SQLCOM_DELETE:
- {
- if ((res= delete_precheck(thd, tables)))
- break;
- res = mysql_delete(thd,tables, select_lex->where,
- &select_lex->order_list,
- select_lex->select_limit, select_lex->options);
- if (thd->net.report_error)
- res= -1;
- break;
- }
- case SQLCOM_DELETE_MULTI:
- {
- TABLE_LIST *aux_tables=
- (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *target_tbl;
- uint table_count;
- multi_delete *result;
- if ((res= multi_delete_precheck(thd, tables, &table_count)))
- break;
- /* condition will be TRUE on SP re-excuting */
- if (select_lex->item_list.elements != 0)
- select_lex->item_list.empty();
- if (add_item_to_list(thd, new Item_null()))
- {
- res= -1;
- break;
- }
- thd->proc_info="init";
- if ((res=open_and_lock_tables(thd,tables)))
- break;
- /* Fix tables-to-be-deleted-from list to point at opened tables */
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next)
- {
- TABLE_LIST *orig= target_tbl->table_list;
- target_tbl->table= orig->table;
- /*
- Multi-delete can't be constructed over-union => we always have
- single SELECT on top and have to check underlying SELECTs of it
- */
- if (lex->select_lex.check_updateable_in_subqueries(orig->db,
- orig->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- orig->real_name);
- res= -1;
- break;
- }
- }
- if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
- table_count)))
- {
- res= mysql_select(thd, &select_lex->ref_pointer_array,
- select_lex->get_table_list(),
- select_lex->with_wild,
- select_lex->item_list,
- select_lex->where,
- 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
- (ORDER *)NULL,
- select_lex->options | thd->options |
- SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK,
- result, unit, select_lex);
- if (thd->net.report_error)
- res= -1;
- delete result;
- }
- else
- res= -1; // Error is not sent
- close_thread_tables(thd);
- break;
- }
- case SQLCOM_DROP_TABLE:
- {
- if (!lex->drop_temporary)
- {
- if (check_table_access(thd,DROP_ACL,tables,0))
- goto error; /* purecov: inspected */
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- }
- else
- {
- /*
- If this is a slave thread, we may sometimes execute some
- DROP / * 40005 TEMPORARY * / TABLE
- that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE
- MASTER TO), while the temporary table has already been dropped.
- To not generate such irrelevant "table does not exist errors",
- we silently add IF EXISTS if TEMPORARY was used.
- */
- if (thd->slave_thread)
- lex->drop_if_exists= 1;
- /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
- thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
- }
- res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
- }
- break;
- case SQLCOM_DROP_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
- goto error; /* purecov: inspected */
- if (end_active_trans(thd))
- res= -1;
- else
- res = mysql_drop_index(thd, tables, &lex->alter_info);
- break;
- case SQLCOM_SHOW_DATABASES:
- #if defined(DONT_ALLOW_SHOW_COMMANDS)
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
- check_global_access(thd, SHOW_DB_ACL))
- goto error;
- res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS));
- break;
- #endif
- case SQLCOM_SHOW_PROCESSLIST:
- if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
- break;
- mysqld_list_processes(thd,
- thd->master_access & PROCESS_ACL ? NullS :
- thd->priv_user,lex->verbose);
- break;
- case SQLCOM_SHOW_STORAGE_ENGINES:
- res= mysqld_show_storage_engines(thd);
- break;
- case SQLCOM_SHOW_PRIVILEGES:
- res= mysqld_show_privileges(thd);
- break;
- case SQLCOM_SHOW_COLUMN_TYPES:
- res= mysqld_show_column_types(thd);
- break;
- case SQLCOM_SHOW_STATUS:
- res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
- OPT_GLOBAL, &LOCK_status);
- break;
- case SQLCOM_SHOW_VARIABLES:
- res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
- init_vars, lex->option_type,
- &LOCK_global_system_variables);
- break;
- case SQLCOM_SHOW_LOGS:
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
- goto error;
- res= mysqld_show_logs(thd);
- break;
- }
- #endif
- case SQLCOM_SHOW_TABLES:
- /* FALL THROUGH */
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- char *db=select_lex->db ? select_lex->db : thd->db;
- if (!db)
- {
- send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
- goto error; /* purecov: inspected */
- }
- remove_escape(db); // Fix escaped '_'
- if (check_db_name(db))
- {
- net_printf(thd,ER_WRONG_DB_NAME, db);
- goto error;
- }
- if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0))
- goto error; /* purecov: inspected */
- if (!thd->col_access && check_grant_db(thd,db))
- {
- net_printf(thd, ER_DBACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- db);
- goto error;
- }
- /* grant is checked in mysqld_show_tables */
- if (lex->describe)
- res= mysqld_extend_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
- else
- res= mysqld_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
- break;
- }
- #endif
- case SQLCOM_SHOW_OPEN_TABLES:
- res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_CHARSETS:
- res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_COLLATIONS:
- res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_FIELDS:
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- char *db=tables->db;
- remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
- if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
- goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
- goto error;
- res= mysqld_show_fields(thd,tables,
- (lex->wild ? lex->wild->ptr() : NullS),
- lex->verbose);
- break;
- }
- #endif
- case SQLCOM_SHOW_KEYS:
- #ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
- #else
- {
- char *db=tables->db;
- remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
- if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
- goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
- goto error;
- res= mysqld_show_keys(thd,tables);
- break;
- }
- #endif
- case SQLCOM_CHANGE_DB:
- mysql_change_db(thd,select_lex->db);
- break;
- case SQLCOM_LOAD:
- {
- uint privilege= (lex->duplicates == DUP_REPLACE ?
- INSERT_ACL | DELETE_ACL : INSERT_ACL);
- if (!lex->local_file)
- {
- if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
- goto error;
- }
- else
- {
- if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
- ! opt_local_infile)
- {
- send_error(thd,ER_NOT_ALLOWED_COMMAND);
- goto error;
- }
- if (check_one_table_access(thd, privilege, tables))
- goto error;
- }
- res=mysql_load(thd, lex->exchange, tables, lex->field_list,
- lex->duplicates, lex->ignore, (bool) lex->local_file, lex->lock_option);
- break;
- }
- case SQLCOM_SET_OPTION:
- {
- List<set_var_base> *lex_var_list= &lex->var_list;
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
- break;
- if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
- {
- my_printf_error(0, "The SET ONE_SHOT syntax is reserved for
- purposes internal to the MySQL server", MYF(0));
- res= -1;
- break;
- }
- if (!(res= sql_set_variables(thd, lex_var_list)))
- {
- /*
- If the previous command was a SET ONE_SHOT, we don't want to forget
- about the ONE_SHOT property of that SET. So we use a |= instead of = .
- */
- thd->one_shot_set|= lex->one_shot_set;
- send_ok(thd);
- }
- if (thd->net.report_error)
- res= -1;
- break;
- }
- case SQLCOM_UNLOCK_TABLES:
- /*
- It is critical for mysqldump --single-transaction --master-data that
- UNLOCK TABLES does not implicitely commit a connection which has only
- done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
- false, mysqldump will not work.
- */
- unlock_locked_tables(thd);
- if (thd->options & OPTION_TABLE_LOCK)
- {
- end_active_trans(thd);
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
- }
- if (thd->global_read_lock)
- unlock_global_read_lock(thd);
- send_ok(thd);
- break;
- case SQLCOM_LOCK_TABLES:
- unlock_locked_tables(thd);
- if (check_db_used(thd,tables) || end_active_trans(thd))
- goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
- goto error;
- thd->in_lock_tables=1;
- thd->options|= OPTION_TABLE_LOCK;
- if (!(res= open_and_lock_tables(thd, tables)))
- {
- #ifdef HAVE_QUERY_CACHE
- if (thd->variables.query_cache_wlock_invalidate)
- query_cache.invalidate_locked_for_write(tables);
- #endif /*HAVE_QUERY_CACHE*/
- thd->locked_tables=thd->lock;
- thd->lock=0;
- send_ok(thd);
- }
- else
- thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
- thd->in_lock_tables=0;
- break;
- case SQLCOM_CREATE_DB:
- {
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- char *alias;
- if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
- {
- net_printf(thd,ER_WRONG_DB_NAME, lex->name);
- break;
- }
- /*
- If in a slave thread :
- CREATE DATABASE DB was certainly not preceded by USE DB.
- For that reason, db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
- #ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(lex->name)))
- {
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
- }
- #endif
- if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
- break;
- res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
- &lex->create_info, 0);
- break;
- }
- case SQLCOM_DROP_DB:
- {
- if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
- char *alias;
- if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
- {
- net_printf(thd, ER_WRONG_DB_NAME, lex->name);
- break;
- }
- /*
- If in a slave thread :
- DROP DATABASE DB may not be preceded by USE DB.
- For that reason, maybe db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
- #ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(lex->name)))
- {
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
- }
- #endif
- if (check_access(thd,DROP_ACL,lex->name,0,1,0))
- break;
- if (thd->locked_tables || thd->active_transaction())
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- goto error;
- }
- res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name),
- lex->drop_if_exists, 0);
- break;
- }
- case SQLCOM_ALTER_DB:
- {
- char *db= lex->name ? lex->name : thd->db;
- if (!db)
- {
- send_error(thd, ER_NO_DB_ERROR);
- goto error;
- }
- if (!strip_sp(db) || check_db_name(db))
- {
- net_printf(thd, ER_WRONG_DB_NAME, db);
- break;
- }
- /*
- If in a slave thread :
- ALTER DATABASE DB may not be preceded by USE DB.
- For that reason, maybe db_ok() in sql/slave.cc did not check the
- do_db/ignore_db. And as this query involves no tables, tables_ok()
- above was not called. So we have to check rules again here.
- */
- #ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!db_ok(db, replicate_do_db, replicate_ignore_db) ||
- !db_ok_with_wild_table(db)))
- {
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
- }
- #endif
- if (check_access(thd, ALTER_ACL, db, 0, 1, 0))
- break;
- if (thd->locked_tables || thd->active_transaction())
- {
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- goto error;
- }
- res= mysql_alter_db(thd, db, &lex->create_info);
- break;
- }
- case SQLCOM_SHOW_CREATE_DB:
- {
- if (!strip_sp(lex->name) || check_db_name(lex->name))
- {
- net_printf(thd,ER_WRONG_DB_NAME, lex->name);
- break;
- }
- if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
- break;
- res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
- break;
- }
- case SQLCOM_CREATE_FUNCTION:
- if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
- break;
- #ifdef HAVE_DLOPEN
- if (!(res = mysql_create_function(thd,&lex->udf)))
- send_ok(thd);
- #else
- net_printf(thd, ER_CANT_OPEN_LIBRARY, lex->udf.dl, 0, "feature disabled");
- res= -1;
- #endif
- break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
- break;
- #ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
- #else
- res= -1;
- #endif
- break;
- #ifndef NO_EMBEDDED_ACCESS_CHECKS
- case SQLCOM_DROP_USER:
- {
- if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
- break;
- if (!(res= mysql_drop_user(thd, lex->users_list)))
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd);
- }
- break;
- }
- case SQLCOM_REVOKE_ALL:
- {
- if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
- break;
- if (!(res = mysql_revoke_all(thd, lex->users_list)))
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- send_ok(thd);
- }
- break;
- }
- case SQLCOM_REVOKE:
- case SQLCOM_GRANT:
- {
- if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables ? tables->db : select_lex->db,
- tables ? &tables->grant.privilege : 0,
- tables ? 0 : 1, 0))
- goto error;
- /*
- Check that the user isn't trying to change a password for another
- user if he doesn't have UPDATE privilege to the MySQL database
- */
- if (thd->user) // If not replication
- {
- LEX_USER *user;
- List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user=user_list++))
- {
- if (user->password.str &&
- (strcmp(thd->user,user->user.str) ||
- user->host.str &&
- my_strcasecmp(&my_charset_latin1,
- user->host.str, thd->host_or_ip)))
- {
- if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1))
- {
- send_error(thd, ER_PASSWORD_NOT_ALLOWED);
- goto error;
- }
- break; // We are allowed to do global changes
- }
- }
- }
- if (specialflag & SPECIAL_NO_RESOLVE)
- {
- LEX_USER *user;
- List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user=user_list++))
- {
- if (hostname_requires_resolving(user->host.str))
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_HOSTNAME_WONT_WORK,
- ER(ER_WARN_HOSTNAME_WONT_WORK),
- user->host.str);
- }
- }
- if (tables)
- {
- if (grant_option && check_grant(thd,
- (lex->grant | lex->grant_tot_col |
- GRANT_ACL),
- tables, 0, UINT_MAX, 0))
- goto error;
- if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
- lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- }
- }
- else
- {
- if (lex->columns.elements)
- {
- send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
- res=1;
- }
- else
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE);
- if (!res)
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- if (mqh_used && lex->sql_command == SQLCOM_GRANT)
- {
- List_iterator <LEX_USER> str_list(lex->users_list);
- LEX_USER *user;
- while ((user=str_list++))
- reset_mqh(thd,user);
- }
- }
- }
- break;
- }
- #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
- case SQLCOM_RESET:
- /*
- RESET commands are never written to the binary log, so we have to
- initialize this variable because RESET shares the same code as FLUSH
- */
- lex->no_write_to_binlog= 1;
- case SQLCOM_FLUSH:
- {
- if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
- goto error;
- /*
- reload_acl_and_cache() will tell us if we are allowed to write to the
- binlog or not.
- */
- bool write_to_binlog;
- if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog))
- send_error(thd, 0);
- else
- {
- /*
- We WANT to write and we CAN write.
- ! we write after unlocking the table.
- */
- if (!lex->no_write_to_binlog && write_to_binlog)
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- }
- send_ok(thd);
- }
- break;
- }
- case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
- break;
- #ifndef NO_EMBEDDED_ACCESS_CHECKS
- case SQLCOM_SHOW_GRANTS:
- res=0;
- if ((thd->priv_user &&
- !strcmp(thd->priv_user,lex->grant_user->user.str)) ||
- !check_access(thd, SELECT_ACL, "mysql",0,1,0))
- {
- res = mysql_show_grants(thd,lex->grant_user);
- }
- break;
- #endif
- case SQLCOM_HA_OPEN:
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0))
- goto error;
- res = mysql_ha_open(thd, tables);
- break;
- case SQLCOM_HA_CLOSE:
- if (check_db_used(thd,tables))
- goto error;
- res = mysql_ha_close(thd, tables);
- break;
- case SQLCOM_HA_READ:
- /*
- There is no need to check for table permissions here, because
- if a user has no permissions to read a table, he won't be
- able to open it (with SQLCOM_HA_OPEN) in the first place.
- */
- if (check_db_used(thd,tables))
- goto error;
- res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
- lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- select_lex->select_limit, select_lex->offset_limit);
- break;
- case SQLCOM_BEGIN:
- if (thd->locked_tables)
- {
- thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automaticly closed
- close_thread_tables(thd); // Free tables
- }
- if (end_active_trans(thd))
- {
- res= -1;
- }
- else
- {
- thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
- OPTION_BEGIN);
- thd->server_status|= SERVER_STATUS_IN_TRANS;
- if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
- !(res= ha_start_consistent_snapshot(thd)))
- send_ok(thd);
- }
- break;
- case SQLCOM_COMMIT:
- /*
- We don't use end_active_trans() here to ensure that this works
- even if there is a problem with the OPTION_AUTO_COMMIT flag
- (Which of course should never happen...)
- */
- {
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (!ha_commit(thd))
- {
- send_ok(thd);
- }
- else
- res= -1;
- break;
- }
- case SQLCOM_ROLLBACK:
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (!ha_rollback(thd))
- {
- /*
- If a non-transactional table was updated, warn; don't warn if this is a
- slave thread (because when a slave thread executes a ROLLBACK, it has
- been read from the binary log, so it's 100% sure and normal to produce
- error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
- slave SQL thread, it would not stop the thread but just be printed in
- the error log; but we don't want users to wonder why they have this
- message in the error log, so we don't send it.
- */
- if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
- send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
- else
- send_ok(thd);
- }
- else
- res= -1;
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
- break;
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
- {
- if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
- send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0);
- else
- send_ok(thd);
- }
- else
- res= -1;
- break;
- case SQLCOM_SAVEPOINT:
- if (!ha_savepoint(thd, lex->savepoint_name))
- send_ok(thd);
- else
- res= -1;
- break;
- default: /* Impossible */
- send_ok(thd);
- break;
- }
- thd->proc_info="query end"; // QQ
- /*
- Reset system variables temporarily modified by SET ONE SHOT.
- Exception: If this is a SET, do nothing. This is to allow
- mysqlbinlog to print many SET commands (in this case we want the
- charset temp setting to live until the real query). This is also
- needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
- immediately.
- */
- if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
- reset_one_shot_variables(thd);
- if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
- error:
- DBUG_VOID_RETURN;
- }
- /*
- Check grants for commands which work only with one table and all other
- tables belonging to subselects or implicitly opened tables.
- SYNOPSIS
- check_one_table_access()
- thd Thread handler
- privilege requested privelage
- tables table list of command
- RETURN
- 0 - OK
- 1 - access denied, error is sent to client
- */
- int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
- {
- if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
- return 1;
- /* Show only 1 table for check_grant */
- if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
- return 1;
- /* Check rights on tables of subselects and implictly opened tables */
- TABLE_LIST *subselects_tables;
- if ((subselects_tables= tables->next))
- {
- if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
- return 1;
- }
- return 0;
- }
- /****************************************************************************
- Get the user (global) and database privileges for all used tables
- NOTES
- The idea of EXTRA_ACL is that one will be granted access to the table if
- one has the asked privilege on any column combination of the table; For
- example to be able to check a table one needs to have SELECT privilege on
- any column of the table.
- RETURN
- 0 ok
- 1 If we can't get the privileges and we don't use table/column grants.
- save_priv In this we store global and db level grants for the table
- Note that we don't store db level grants if the global grants
- is enough to satisfy the request and the global grants contains
- a SELECT grant.
- ****************************************************************************/
- bool
- check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
- bool dont_check_global_grants, bool no_errors)
- {
- DBUG_ENTER("check_access");
- DBUG_PRINT("enter",("db: '%s' want_access: %lu master_access: %lu",
- db ? db : "", want_access, thd->master_access));
- #ifndef NO_EMBEDDED_ACCESS_CHECKS
- ulong db_access;
- bool db_is_pattern= test(want_access & GRANT_ACL);
- #endif
- ulong dummy;
- if (save_priv)
- *save_priv=0;
- else
- save_priv= &dummy;
- if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
- {
- if (!no_errors)
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
- DBUG_RETURN(TRUE); /* purecov: tested */
- }
- #ifdef NO_EMBEDDED_ACCESS_CHECKS
- DBUG_RETURN(0);
- #else
- if ((thd->master_access & want_access) == want_access)
- {
- /*
- If we don't have a global SELECT privilege, we have to get the database
- specific access rights to be able to handle queries of type
- UPDATE t1 SET a=1 WHERE b > 0
- */
- db_access= thd->db_access;
- if (!(thd->master_access & SELECT_ACL) &&
- (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
- db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
- *save_priv=thd->master_access | db_access;
- DBUG_RETURN(FALSE);
- }
- if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
- ! db && dont_check_global_grants)
- { // We can never grant this
- if (!no_errors)
- net_printf(thd,ER_ACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
- DBUG_RETURN(TRUE); /* purecov: tested */
- }
- if (db == any_db)
- DBUG_RETURN(FALSE); // Allow select on anything
- if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
- db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
- 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);
- db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
- /* grant_option is set if there exists a single table or column grant */
- if (db_access == want_access ||
- ((grant_option && !dont_check_global_grants) &&
- !(want_access & ~(db_access | TABLE_ACLS))))
- DBUG_RETURN(FALSE); /* Ok */
- if (!no_errors)
- net_printf(thd,ER_DBACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
- DBUG_RETURN(TRUE); /* purecov: tested */
- #endif /* NO_EMBEDDED_ACCESS_CHECKS */
- }
- /*
- check for global access and give descriptive error message if it fails
- SYNOPSIS
- check_global_access()
- thd Thread handler
- want_access Use should have any of these global rights
- WARNING
- One gets access rigth if one has ANY of the rights in want_access
- This is useful as one in most cases only need one global right,
- but in some case we want to check if the user has SUPER or
- REPL_CLIENT_ACL rights.
- RETURN
- 0 ok
- 1 Access denied. In this case an error is sent to the client
- */
- bool check_global_access(THD *thd, ulong want_access)
- {
- #ifdef NO_EMBEDDED_ACCESS_CHECKS
- return 0;
- #else
- char command[128];
- if ((thd->master_access & want_access))
- return 0;
- get_privilege_desc(command, sizeof(command), want_access);
- net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
- command);
- return 1;
- #endif /* NO_EMBEDDED_ACCESS_CHECKS */
- }
- /*
- Check the privilege for all used tables. Table privileges are cached
- in the table list for GRANT checking
- */
- bool
- check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
- bool no_errors)
- {
- uint found=0;
- ulong found_access=0;
- TABLE_LIST *org_tables=tables;
- for (; tables ; tables=tables->next)
- {
- 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)
- tables->grant.privilege= want_access;
- else if (tables->db && tables->db == thd->db)
- {
- if (found && !grant_option) // db already checked
- tables->grant.privilege=found_access;
- else
- {
- if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
- 0, no_errors))
- return TRUE; // Access denied
- found_access=tables->grant.privilege;
- found=1;
- }
- }
- else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
- 0, no_errors))
- return TRUE;
- }
- if (grant_option)
- return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
- test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
- return FALSE;
- }
- bool check_merge_table_access(THD *thd, char *db,
- TABLE_LIST *table_list)
- {
- int error=0;
- if (table_list)
- {
- /* Check that all tables use the current database */
- TABLE_LIST *tmp;
- for (tmp=table_list; tmp ; tmp=tmp->next)
- {
- if (!tmp->db || !tmp->db[0])
- tmp->db=db;
- }
- error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- table_list,0);
- }
- return error;
- }
- static bool check_db_used(THD *thd,TABLE_LIST *tables)
- {
- for (; tables ; tables=tables->next)
- {
- if (!tables->db)
- {
- if (!(tables->db=thd->db))
- {
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
- return TRUE; /* purecov: tested */
- }
- }
- }
- return FALSE;
- }
- /****************************************************************************
- Check stack size; Send error if there isn't enough stack to continue
- ****************************************************************************/
- #if STACK_DIRECTION < 0
- #define used_stack(A,B) (long) (A - B)
- #else
- #define used_stack(A,B) (long) (B - A)
- #endif
- #ifndef DBUG_OFF
- long max_stack_used;
- #endif
- #ifndef EMBEDDED_LIBRARY
- bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
- {
- long stack_used;
- if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
- (long) thread_stack_min)
- {
- sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack);
- my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0));
- thd->fatal_error();
- return 1;
- }
- #ifndef DBUG_OFF
- max_stack_used= max(max_stack_used, stack_used);
- #endif
- return 0;
- }
- #endif /* EMBEDDED_LIBRARY */
- #define MY_YACC_INIT 1000 // Start with big alloc
- #define MY_YACC_MAX 32000 // Because of 'short'
- bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
- {
- LEX *lex=current_lex;
- ulong old_info=0;
- if ((uint) *yystacksize >= MY_YACC_MAX)
- return 1;
- if (!lex->yacc_yyvs)
- old_info= *yystacksize;
- *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
- if (!(lex->yacc_yyvs= (char*)
- my_realloc((gptr) lex->yacc_yyvs,
- *yystacksize*sizeof(**yyvs),
- MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
- !(lex->yacc_yyss= (char*)
- my_realloc((gptr) lex->yacc_yyss,
- *yystacksize*sizeof(**yyss),
- MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
- return 1;
- if (old_info)
- { // Copy old info from stack
- memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
- memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
- }
- *yyss=(short*) lex->yacc_yyss;
- *yyvs=(YYSTYPE*) lex->yacc_yyvs;
- return 0;
- }
- /****************************************************************************
- Initialize global thd variables needed for query
- ****************************************************************************/
- void
- mysql_init_query(THD *thd, uchar *buf, uint length)
- {
- DBUG_ENTER("mysql_init_query");
- lex_start(thd, buf, length);
- 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;
- }
- void
- mysql_init_select(LEX *lex)
- {
- SELECT_LEX *select_lex= lex->current_select;
- select_lex->init_select();
- select_lex->select_limit= HA_POS_ERROR;
- if (select_lex == &lex->select_lex)
- {
- DBUG_ASSERT(lex->result == 0);
- lex->exchange= 0;
- }
- }
- bool
- mysql_new_select(LEX *lex, bool move_down)
- {
- SELECT_LEX *select_lex;
- if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
- return 1;
- select_lex->select_number= ++lex->thd->select_number;
- select_lex->init_query();
- select_lex->init_select();
- /*
- Don't evaluate this subquery during statement prepare even if
- it's a constant one. The flag is switched off in the end of
- mysql_stmt_prepare.
- */
- if (lex->thd->current_arena->is_stmt_prepare())
- select_lex->uncacheable|= UNCACHEABLE_PREPARE;
- if (move_down)
- {
- lex->subqueries= TRUE;
- /* first select_lex of subselect or derived table */
- SELECT_LEX_UNIT *unit;
- if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
- return 1;
- unit->init_query();
- unit->init_select();
- unit->thd= lex->thd;
- unit->include_down(lex->current_select);
- unit->link_next= 0;
- unit->link_prev= 0;
- unit->return_to= lex->current_select;
- select_lex->include_down(unit);
- // TODO: assign resolve_mode for fake subquery after merging with new tree
- }
- else
- {
- select_lex->include_neighbour(lex->current_select);
- SELECT_LEX_UNIT *unit= select_lex->master_unit();
- SELECT_LEX *fake= unit->fake_select_lex;
- if (!fake)
- {
- /*
- as far as we included SELECT_LEX for UNION unit should have
- fake SELECT_LEX for UNION processing
- */
- if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
- return 1;
- fake->include_standalone(unit,
- (SELECT_LEX_NODE**)&unit->fake_select_lex);
- fake->select_number= INT_MAX;
- fake->make_empty_select();
- fake->linkage= GLOBAL_OPTIONS_TYPE;
- fake->select_limit= HA_POS_ERROR;
- }
- }
- select_lex->master_unit()->global_parameters= select_lex;
- select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
- lex->current_select= select_lex;
- select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- return 0;
- }
- /*
- Create a select to return the same output as 'SELECT @@var_name'.
- SYNOPSIS
- create_select_for_variable()
- var_name Variable name
- DESCRIPTION
- Used for SHOW COUNT(*) [ WARNINGS | ERROR]
- This will crash with a core dump if the variable doesn't exists
- */
- void create_select_for_variable(const char *var_name)
- {
- THD *thd;
- LEX *lex;
- LEX_STRING tmp, null_lex_string;
- Item *var;
- char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
- DBUG_ENTER("create_select_for_variable");
- thd= current_thd;
- lex= thd->lex;
- mysql_init_select(lex);
- lex->sql_command= SQLCOM_SELECT;
- tmp.str= (char*) var_name;
- tmp.length=strlen(var_name);
- bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
- /*
- We set the name of Item to @@session.var_name because that then is used
- as the column name in the output.
- */
- if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
- {
- end= strxmov(buff, "@@session.", var_name, NullS);
- var->set_name(buff, end-buff, system_charset_info);
- add_item_to_list(thd, var);
- }
- DBUG_VOID_RETURN;
- }
- static TABLE_LIST* get_table_by_alias(TABLE_LIST* tl, const char* db,
- const char* alias)
- {
- for (;tl;tl= tl->next)
- {
- if (!strcmp(db,tl->db) &&
- tl->alias && !my_strcasecmp(table_alias_charset,tl->alias,alias))
- return tl;
- }
-
- return 0;
- }
- /* Sets up lex->auxilliary_table_list */
- void fix_multi_delete_lex(LEX* lex)
- {
- TABLE_LIST *tl;
- TABLE_LIST *good_list= (TABLE_LIST*)lex->select_lex.table_list.first;
-
- for (tl= (TABLE_LIST*)lex->auxilliary_table_list.first; tl; tl= tl->next)
- {
- TABLE_LIST* good_table= get_table_by_alias(good_list,tl->db,tl->alias);
- if (good_table && !good_table->derived)
- {
- /*
- real_name points to a member of Table_ident which is
- allocated via thd->strmake() from THD memroot
- */
- tl->real_name= good_table->real_name;
- tl->real_name_length= good_table->real_name_length;
- good_table->updating= tl->updating;
- }
- }
- }
- void mysql_init_multi_delete(LEX *lex)
- {
- lex->sql_command= SQLCOM_DELETE_MULTI;
- mysql_init_select(lex);
- lex->select_lex.select_limit= lex->unit.select_limit_cnt=
- HA_POS_ERROR;
- lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
- lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
- }
- /*
- When you modify mysql_parse(), you may need to mofify
- mysql_test_parse_for_slave() in this same file.
- */
- void mysql_parse(THD *thd, char *inBuf, uint length)
- {
- DBUG_ENTER("mysql_parse");
- mysql_init_query(thd, (uchar*) inBuf, length);
- if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
- {
- LEX *lex= thd->lex;
- if (!yyparse((void *)thd) && ! thd->is_fatal_error)
- {
- #ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (mqh_used && thd->user_connect &&
- check_mqh(thd, lex->sql_command))
- {
- thd->net.error = 0;
- }
- else
- #endif
- {
- if (thd->net.report_error)
- send_error(thd, 0, NullS);
- else
- {
- /*
- Binlog logs a string starting from thd->query and having length
- thd->query_length; so we set thd->query_length correctly (to not
- log several statements in one event, when we executed only first).
- We set it to not see the ';' (otherwise it would get into binlog
- and Query_log_event::print() would give ';;' output).
- This also helps display only the current query in SHOW
- PROCESSLIST.
- Note that we don't need LOCK_thread_count to modify query_length.
- */
- if (lex->found_colon &&
- (thd->query_length= (ulong)(lex->found_colon - thd->query)))
- thd->query_length--;
- /* Actually execute the query */
- mysql_execute_command(thd);
- query_cache_end_of_result(thd);
- }
- }
- }
- else
- {
- DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
- thd->is_fatal_error));
- query_cache_abort(&thd->net);
- }
- thd->proc_info="freeing items";
- thd->end_statement();
- DBUG_ASSERT(thd->change_list.is_empty());
- }
- DBUG_VOID_RETURN;
- }
- #ifdef HAVE_REPLICATION
- /*
- Usable by the replication SQL thread only: just parse a query to know if it
- can be ignored because of replicate-*-table rules.
- RETURN VALUES
- 0 cannot be ignored
- 1 can be ignored
- */
- bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
- {
- LEX *lex= thd->lex;
- bool error= 0;
- mysql_init_query(thd, (uchar*) inBuf, length);
- if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
- all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
- error= 1; /* Ignore question */
- thd->end_statement();
- return error;
- }
- #endif
- /*****************************************************************************
- ** Store field definition for create
- ** Return 0 if ok
- ******************************************************************************/
- bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
- char *length, char *decimals,
- uint type_modifier,
- Item *default_value, Item *on_update_value,
- LEX_STRING *comment,
- char *change,
- List<String> *interval_list, CHARSET_INFO *cs,
- uint uint_geom_type)
- {
- register create_field *new_field;
- LEX *lex= thd->lex;
- uint allowed_type_modifier=0;
- char warn_buff[MYSQL_ERRMSG_SIZE];
- DBUG_ENTER("add_field_to_list");
- if (strlen(field_name) > NAME_LEN)
- {
- net_printf(thd, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- if (type_modifier & PRI_KEY_FLAG)
- {
- lex->col_list.push_back(new key_part_spec(field_name,0));
- lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
- 0, lex->col_list));
- lex->col_list.empty();
- }
- if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
- {
- lex->col_list.push_back(new key_part_spec(field_name,0));
- lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0,
- lex->col_list));
- lex->col_list.empty();
- }
- if (default_value)
- {
- /*
- Default value should be literal => basic constants =>
- no need fix_fields()
-
- We allow only one function as part of default value -
- NOW() as default for TIMESTAMP type.
- */
- if (default_value->type() == Item::FUNC_ITEM &&
- !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
- type == FIELD_TYPE_TIMESTAMP))
- {
- net_printf(thd, ER_INVALID_DEFAULT, field_name);
- DBUG_RETURN(1);
- }
- else if (default_value->type() == Item::NULL_ITEM)
- {
- default_value= 0;
- if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
- NOT_NULL_FLAG)
- {
- net_printf(thd,ER_INVALID_DEFAULT,field_name);
- DBUG_RETURN(1);
- }
- }
- else if (type_modifier & AUTO_INCREMENT_FLAG)
- {
- net_printf(thd, ER_INVALID_DEFAULT, field_name);
- DBUG_RETURN(1);
- }
- }
- if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
- {
- net_printf(thd, ER_INVALID_ON_UPDATE, field_name);
- DBUG_RETURN(1);
- }
-
- if (!(new_field=new create_field()))
- DBUG_RETURN(1);
- new_field->field=0;
- new_field->field_name=field_name;
- new_field->def= default_value;
- new_field->flags= type_modifier;
- new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
- Field::NEXT_NUMBER : Field::NONE);
- new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0,
- NOT_FIXED_DEC-1) : 0;
- new_field->sql_type=type;
- new_field->length=0;
- new_field->change=change;
- new_field->interval=0;
- new_field->pack_length=0;
- new_field->charset=cs;
- new_field->geom_type= (Field::geometry_type) uint_geom_type;
- if (!comment)
- {
- new_field->comment.str=0;
- new_field->comment.length=0;
- }
- else
- {
- /* In this case comment is always of type Item_string */
- new_field->comment.str= (char*) comment->str;
- new_field->comment.length=comment->length;
- }
- if (length && !(new_field->length= (uint) atoi(length)))
- length=0; /* purecov: inspected */
- uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
- if (new_field->length && new_field->decimals &&
- new_field->length < new_field->decimals+1 &&
- new_field->decimals != NOT_FIXED_DEC)
- new_field->length=new_field->decimals+1; /* purecov: inspected */
- switch (type) {
- case FIELD_TYPE_TINY:
- 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=MAX_SMALLINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_INT24:
- 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=MAX_INT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_LONGLONG:
- if (!length) new_field->length=MAX_BIGINT_WIDTH;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_NULL:
- break;
- case FIELD_TYPE_DECIMAL:
- if (!length)
- {
- if ((new_field->length= new_field->decimals))
- new_field->length++;
- else
- new_field->length= 10; // Default length for DECIMAL
- }
- if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument
- {
- new_field->length+=sign_len;
- if (new_field->decimals)
- new_field->length++;
- }
- break;
- case FIELD_TYPE_STRING:
- case FIELD_TYPE_VAR_STRING:
- if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value)
- break;
- /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */
- new_field->sql_type= FIELD_TYPE_BLOB;
- sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR",
- (cs == &my_charset_bin) ? "BLOB" : "TEXT");
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT,
- warn_buff);
- /* fall through */
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_GEOMETRY:
- if (new_field->length)
- {
- /* The user has given a length to the blob column */
- if (new_field->length < 256)
- type= FIELD_TYPE_TINY_BLOB;
- else if (new_field->length < 65536)
- type= FIELD_TYPE_BLOB;
- else if (new_field->length < 256L*256L*256L)
- type= FIELD_TYPE_MEDIUM_BLOB;
- else
- type= FIELD_TYPE_LONG_BLOB;
- new_field->length= 0;
- }
- new_field->sql_type= type;
- if (default_value) // Allow empty as default value
- {
- String str,*res;
- res=default_value->val_str(&str);
- if (res->length())
- {
- net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- new_field->def=0;
- }
- new_field->flags|=BLOB_FLAG;
- break;
- case FIELD_TYPE_YEAR:
- if (!length || new_field->length != 2)
- new_field->length=4; // Default length
- new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- break;
- case FIELD_TYPE_FLOAT:
- /* change FLOAT(precision) to FLOAT or DOUBLE */
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (length && !decimals)
- {
- uint tmp_length=new_field->length;
- if (tmp_length > PRECISION_FOR_DOUBLE)
- {
- net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
- DBUG_RETURN(1);
- }
- else if (tmp_length > PRECISION_FOR_FLOAT)
- {
- new_field->sql_type=FIELD_TYPE_DOUBLE;
- new_field->length=DBL_DIG+7; // -[digits].E+###
- }
- else
- new_field->length=FLT_DIG+6; // -[digits].E+##
- new_field->decimals= NOT_FIXED_DEC;
- break;
- }
- if (!length)
- {
- new_field->length = FLT_DIG+6;
- new_field->decimals= NOT_FIXED_DEC;
- }
- break;
- case FIELD_TYPE_DOUBLE:
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (!length)
- {
- new_field->length = DBL_DIG+7;
- new_field->decimals=NOT_FIXED_DEC;
- }
- break;
- case FIELD_TYPE_TIMESTAMP:
- if (!length)
- new_field->length= 14; // Full date YYYYMMDDHHMMSS
- else if (new_field->length != 19)
- {
- /*
- We support only even TIMESTAMP lengths less or equal than 14
- and 19 as length of 4.1 compatible representation.
- */
- 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;
- if (default_value)
- {
- /* Grammar allows only NOW() value for ON UPDATE clause */
- if (default_value->type() == Item::FUNC_ITEM &&
- ((Item_func*)default_value)->functype() == Item_func::NOW_FUNC)
- {
- new_field->unireg_check= (on_update_value?Field::TIMESTAMP_DNUN_FIELD:
- Field::TIMESTAMP_DN_FIELD);
- /*
- We don't need default value any longer moreover it is dangerous.
- Everything handled by unireg_check further.
- */
- new_field->def= 0;
- }
- else
- new_field->unireg_check= (on_update_value?Field::TIMESTAMP_UN_FIELD:
- Field::NONE);
- }
- else
- {
- /*
- 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 :
- (new_field->flags & NOT_NULL_FLAG ?
- Field::TIMESTAMP_OLD_FIELD:
- Field::NONE);
- }
- break;
- case FIELD_TYPE_DATE: // Old date type
- if (protocol_version != PROTOCOL_VERSION-1)
- new_field->sql_type=FIELD_TYPE_NEWDATE;
- /* fall trough */
- case FIELD_TYPE_NEWDATE:
- new_field->length=10;
- break;
- case FIELD_TYPE_TIME:
- new_field->length=10;
- break;
- case FIELD_TYPE_DATETIME:
- new_field->length=19;
- break;
- case FIELD_TYPE_SET:
- {
- if (interval_list->elements > sizeof(longlong)*8)
- {
- net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- new_field->pack_length= get_set_pack_length(interval_list->elements);
- List_iterator<String> it(*interval_list);
- String *tmp;
- while ((tmp= it++))
- new_field->interval_list.push_back(tmp);
- /*
- Set fake length to 1 to pass the below conditions.
- Real length will be set in mysql_prepare_table()
- when we know the character set of the column
- */
- new_field->length= 1;
- }
- break;
- case FIELD_TYPE_ENUM:
- {
- // Should be safe
- new_field->pack_length= get_enum_pack_length(interval_list->elements);
- List_iterator<String> it(*interval_list);
- String *tmp;
- while ((tmp= it++))
- new_field->interval_list.push_back(tmp);
- new_field->length= 1; // See comment for FIELD_TYPE_SET above.
- }
- break;
- }
- if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET &&
- type != FIELD_TYPE_ENUM) ||
- (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
- type != FIELD_TYPE_STRING &&
- type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
- {
- net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
- MAX_FIELD_CHARLENGTH); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- type_modifier&= AUTO_INCREMENT_FLAG;
- if ((~allowed_type_modifier) & type_modifier)
- {
- net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
- DBUG_RETURN(1);
- }
- if (!new_field->pack_length)
- new_field->pack_length=calc_pack_length(new_field->sql_type ==
- FIELD_TYPE_VAR_STRING ?
- FIELD_TYPE_STRING :
- new_field->sql_type,
- new_field->length);
- lex->create_list.push_back(new_field);
- lex->last_field=new_field;
- DBUG_RETURN(0);
- }
- /* Store position for column in ALTER TABLE .. ADD column */
- void store_position_for_column(const char *name)
- {
- current_lex->last_field->after=my_const_cast(char*) (name);
- }
- bool
- add_proc_to_list(THD* thd, Item *item)
- {
- ORDER *order;
- Item **item_ptr;
- if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
- return 1;
- item_ptr = (Item**) (order+1);
- *item_ptr= item;
- order->item=item_ptr;
- order->free_me=0;
- thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
- return 0;
- }
- /* Fix escaping of _, % and in database and table names (for ODBC) */
- static void remove_escape(char *name)
- {
- if (!*name) // For empty DB names
- return;
- char *to;
- #ifdef USE_MB
- char *strend=name+(uint) strlen(name);
- #endif
- for (to=name; *name ; name++)
- {
- #ifdef USE_MB
- int l;
- /* if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
- if (use_mb(system_charset_info) &&
- (l = my_ismbchar(system_charset_info, name, strend)))
- {
- while (l--)
- *to++ = *name++;
- name--;
- continue;
- }
- #endif
- if (*name == '\' && name[1])
- name++; // Skip '\'
- *to++= *name;
- }
- *to=0;
- }
- /****************************************************************************
- ** save order by and tables in own lists
- ****************************************************************************/
- bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
- {
- ORDER *order;
- DBUG_ENTER("add_to_list");
- if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
- DBUG_RETURN(1);
- order->item_ptr= item;
- order->item= &order->item_ptr;
- order->asc = asc;
- order->free_me=0;
- order->used=0;
- list.link_in_list((byte*) order,(byte**) &order->next);
- DBUG_RETURN(0);
- }
- /*
- Add a table to list of used tables
- SYNOPSIS
- add_table_to_list()
- table Table to add
- alias alias for table (or null if no alias)
- table_options A set of the following bits:
- TL_OPTION_UPDATING Table will be updated
- TL_OPTION_FORCE_INDEX Force usage of index
- lock_type How table should be locked
- use_index List of indexed used in USE INDEX
- ignore_index List of indexed used in IGNORE INDEX
- RETURN
- 0 Error
- # Pointer to TABLE_LIST element added to the total table list
- */
- TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
- Table_ident *table,
- LEX_STRING *alias,
- ulong table_options,
- thr_lock_type lock_type,
- List<String> *use_index_arg,
- List<String> *ignore_index_arg,
- LEX_STRING *option)
- {
- register TABLE_LIST *ptr;
- char *alias_str;
- DBUG_ENTER("add_table_to_list");
- if (!table)
- DBUG_RETURN(0); // End of memory
- alias_str= alias ? alias->str : table->table.str;
- if (check_table_name(table->table.str,table->table.length) ||
- table->db.str && check_db_name(table->db.str))
- {
- net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str);
- DBUG_RETURN(0);
- }
- if (!alias) /* Alias is case sensitive */
- {
- if (table->sel)
- {
- net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
- DBUG_RETURN(0);
- }
- if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
- DBUG_RETURN(0);
- }
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
- DBUG_RETURN(0); /* purecov: inspected */
- if (table->db.str)
- {
- ptr->db= table->db.str;
- ptr->db_length= table->db.length;
- }
- else if (thd->db)
- {
- ptr->db= thd->db;
- ptr->db_length= thd->db_length;
- }
- else
- {
- /* The following can't be "" as we may do 'casedn_str()' on it */
- 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)
- my_casedn_str(files_charset_info, table->table.str);
- ptr->real_name=table->table.str;
- ptr->real_name_length=table->table.length;
- ptr->lock_type= lock_type;
- ptr->updating= test(table_options & TL_OPTION_UPDATING);
- ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
- ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
- ptr->derived= table->sel;
- ptr->cacheable_table= 1;
- if (use_index_arg)
- ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
- sizeof(*use_index_arg));
- if (ignore_index_arg)
- ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg,
- sizeof(*ignore_index_arg));
- ptr->option= option ? option->str : 0;
- /* check that used name is unique */
- if (lock_type != TL_IGNORE)
- {
- for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
- tables ;
- tables=tables->next)
- {
- if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
- !strcmp(ptr->db, tables->db))
- {
- net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */
- DBUG_RETURN(0); /* purecov: tested */
- }
- }
- }
- table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
- DBUG_RETURN(ptr);
- }
- /*
- Set lock for all tables in current select level
- SYNOPSIS:
- set_lock_for_tables()
- lock_type Lock to set for tables
- NOTE:
- If lock is a write lock, then tables->updating is set 1
- This is to get tables_ok to know that the table is updated by the
- query
- */
- void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
- {
- bool for_update= lock_type >= TL_READ_NO_INSERT;
- DBUG_ENTER("set_lock_for_tables");
- DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
- for_update));
- for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
- tables ;
- tables=tables->next)
- {
- tables->lock_type= lock_type;
- tables->updating= for_update;
- }
- DBUG_VOID_RETURN;
- }
- void add_join_on(TABLE_LIST *b,Item *expr)
- {
- if (expr)
- {
- if (!b->on_expr)
- b->on_expr=expr;
- else
- {
- // This only happens if you have both a right and left join
- b->on_expr=new Item_cond_and(b->on_expr,expr);
- }
- b->on_expr->top_level_item();
- }
- }
- /*
- Mark that we have a NATURAL JOIN between two tables
- SYNOPSIS
- add_join_natural()
- a Table to do normal join with
- b Do normal join with this table
-
- IMPLEMENTATION
- This function just marks that table b should be joined with a.
- The function setup_cond() will create in b->on_expr a list
- of equal condition between all fields of the same name.
- SELECT * FROM t1 NATURAL LEFT JOIN t2
- <=>
- SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
- */
- void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
- {
- b->natural_join=a;
- }
- /*
- Reload/resets privileges and the different caches.
- SYNOPSIS
- reload_acl_and_cache()
- thd Thread handler
- options What should be reset/reloaded (tables, privileges,
- slave...)
- tables Tables to flush (if any)
- write_to_binlog Depending on 'options', it may be very bad to write the
- query to the binlog (e.g. FLUSH SLAVE); this is a
- pointer where, if it is not NULL, reload_acl_and_cache()
- will put 0 if it thinks we really should not write to
- the binlog. Otherwise it will put 1.
- RETURN
- 0 ok
- !=0 error
- */
- bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
- bool *write_to_binlog)
- {
- bool result=0;
- select_errors=0; /* Write if more errors */
- bool tmp_write_to_binlog= 1;
- #ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (options & REFRESH_GRANT)
- {
- THD *tmp_thd= 0;
- /*
- If reload_acl_and_cache() is called from SIGHUP handler we have to
- allocate temporary THD for execution of acl_reload()/grant_reload().
- */
- if (!thd && (thd= (tmp_thd= new THD)))
- thd->store_globals();
- if (thd)
- {
- (void)acl_reload(thd);
- (void)grant_reload(thd);
- if (mqh_used)
- reset_mqh(thd, (LEX_USER *) NULL, TRUE);
- }
- if (tmp_thd)
- {
- delete tmp_thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
- thd= 0;
- }
- }
- #endif
- if (options & REFRESH_LOG)
- {
- /*
- Flush the normal query log, the update log, the binary log,
- the slow query log, and the relay log (if it exists).
- */
- /*
- Writing this command to the binlog may result in infinite loops when
- doing mysqlbinlog|mysql, and anyway it does not really make sense to
- log it automatically (would cause more trouble to users than it would
- help them)
- */
- tmp_write_to_binlog= 0;
- mysql_log.new_file(1);
- mysql_update_log.new_file(1);
- mysql_bin_log.new_file(1);
- mysql_slow_log.new_file(1);
- #ifdef HAVE_REPLICATION
- if (mysql_bin_log.is_open() && expire_logs_days)
- {
- long purge_time= time(0) - expire_logs_days*24*60*60;
- if (purge_time >= 0)
- mysql_bin_log.purge_logs_before_date(purge_time);
- }
- pthread_mutex_lock(&LOCK_active_mi);
- rotate_relay_log(active_mi);
- pthread_mutex_unlock(&LOCK_active_mi);
- #endif
- if (ha_flush_logs())
- result=1;
- if (flush_error_log())
- result=1;
- }
- #ifdef HAVE_QUERY_CACHE
- if (options & REFRESH_QUERY_CACHE_FREE)
- {
- query_cache.pack(); // FLUSH QUERY CACHE
- options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
- }
- if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
- {
- query_cache.flush(); // RESET QUERY CACHE
- }
- #endif /*HAVE_QUERY_CACHE*/
- /*
- Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
- (see sql_yacc.yy)
- */
- if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
- {
- if ((options & REFRESH_READ_LOCK) && thd)
- {
- /*
- We must not try to aspire a global read lock if we have a write
- locked table. This would lead to a deadlock when trying to
- reopen (and re-lock) the table after the flush.
- */
- if (thd->locked_tables)
- {
- THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
- THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
- for (; lock_p < end_p; lock_p++)
- {
- if ((*lock_p)->type == TL_WRITE)
- {
- my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
- return 1;
- }
- }
- }
- /*
- Writing to the binlog could cause deadlocks, as we don't log
- UNLOCK TABLES
- */
- tmp_write_to_binlog= 0;
- if (lock_global_read_lock(thd))
- return 1;
- result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
- tables);
- make_global_read_lock_block_commit(thd);
- }
- else
- result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
- my_dbopt_cleanup();
- }
- if (options & REFRESH_HOSTS)
- hostname_cache_refresh();
- if (options & REFRESH_STATUS)
- refresh_status();
- if (options & REFRESH_THREADS)
- flush_thread_cache();
- #ifdef HAVE_REPLICATION
- if (options & REFRESH_MASTER)
- {
- tmp_write_to_binlog= 0;
- if (reset_master(thd))
- result=1;
- }
- #endif
- #ifdef OPENSSL
- if (options & REFRESH_DES_KEY_FILE)
- {
- if (des_key_file)
- result=load_des_key_file(des_key_file);
- }
- #endif
- #ifdef HAVE_REPLICATION
- if (options & REFRESH_SLAVE)
- {
- tmp_write_to_binlog= 0;
- pthread_mutex_lock(&LOCK_active_mi);
- if (reset_slave(thd, active_mi))
- result=1;
- pthread_mutex_unlock(&LOCK_active_mi);
- }
- #endif
- if (options & REFRESH_USER_RESOURCES)
- reset_mqh(thd,(LEX_USER *) NULL);
- if (write_to_binlog)
- *write_to_binlog= tmp_write_to_binlog;
- return result;
- }
- /*
- kill on thread
- SYNOPSIS
- kill_one_thread()
- thd Thread class
- id Thread id
- NOTES
- This is written such that we have a short lock on LOCK_thread_count
- */
- void kill_one_thread(THD *thd, ulong id)
- {
- THD *tmp;
- uint error=ER_NO_SUCH_THREAD;
- VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
- I_List_iterator<THD> it(threads);
- while ((tmp=it++))
- {
- if (tmp->thread_id == id)
- {
- pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
- break;
- }
- }
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- if (tmp)
- {
- if ((thd->master_access & SUPER_ACL) ||
- !strcmp(thd->user,tmp->user))
- {
- tmp->awake(1 /*prepare to die*/);
- error=0;
- }
- else
- error=ER_KILL_DENIED_ERROR;
- pthread_mutex_unlock(&tmp->LOCK_delete);
- }
- if (!error)
- send_ok(thd);
- else
- net_printf(thd,error,id);
- }
- /* Clear most status variables */
- static void refresh_status(void)
- {
- pthread_mutex_lock(&LOCK_status);
- for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
- {
- if (ptr->type == SHOW_LONG)
- *(ulong*) ptr->value= 0;
- }
- /* Reset the counters of all key caches (default and named). */
- process_key_caches(reset_key_cache_counters);
- pthread_mutex_unlock(&LOCK_status);
- }
- /* If pointer is not a null pointer, append filename to it */
- static bool append_file_to_dir(THD *thd, const char **filename_ptr,
- const char *table_name)
- {
- char buff[FN_REFLEN],*ptr, *end;
- if (!*filename_ptr)
- return 0; // nothing to do
- /* Check that the filename is not too long and it's a hard path */
- if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
- !test_if_hard_path(*filename_ptr))
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
- return 1;
- }
- /* Fix is using unix filename format on dos */
- strmov(buff,*filename_ptr);
- end=convert_dirname(buff, *filename_ptr, NullS);
- if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
- return 1; // End of memory
- *filename_ptr=ptr;
- strxmov(ptr,buff,table_name,NullS);
- return 0;
- }
- /*
- Check if the select is a simple select (not an union)
- SYNOPSIS
- check_simple_select()
- RETURN VALUES
- 0 ok
- 1 error ; In this case the error messege is sent to the client
- */
- bool check_simple_select()
- {
- THD *thd= current_thd;
- if (thd->lex->current_select != &thd->lex->select_lex)
- {
- char command[80];
- strmake(command, thd->lex->yylval->symbol.str,
- min(thd->lex->yylval->symbol.length, sizeof(command)-1));
- net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
- return 1;
- }
- return 0;
- }
- Comp_creator *comp_eq_creator(bool invert)
- {
- return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
- }
- Comp_creator *comp_ge_creator(bool invert)
- {
- return invert?(Comp_creator *)<_creator:(Comp_creator *)&ge_creator;
- }
- Comp_creator *comp_gt_creator(bool invert)
- {
- return invert?(Comp_creator *)&le_creator:(Comp_creator *)>_creator;
- }
- Comp_creator *comp_le_creator(bool invert)
- {
- return invert?(Comp_creator *)>_creator:(Comp_creator *)&le_creator;
- }
- Comp_creator *comp_lt_creator(bool invert)
- {
- return invert?(Comp_creator *)&ge_creator:(Comp_creator *)<_creator;
- }
- Comp_creator *comp_ne_creator(bool invert)
- {
- return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
- }
- /*
- Construct ALL/ANY/SOME subquery Item
- SYNOPSIS
- all_any_subquery_creator()
- left_expr - pointer to left expression
- cmp - compare function creator
- all - true if we create ALL subquery
- select_lex - pointer on parsed subquery structure
- RETURN VALUE
- constructed Item (or 0 if out of memory)
- */
- Item * all_any_subquery_creator(Item *left_expr,
- chooser_compare_func_creator cmp,
- bool all,
- SELECT_LEX *select_lex)
- {
- if ((cmp == &comp_eq_creator) && !all) // = ANY <=> IN
- return new Item_in_subselect(left_expr, select_lex);
- if ((cmp == &comp_ne_creator) && all) // <> ALL <=> NOT IN
- return new Item_func_not(new Item_in_subselect(left_expr, select_lex));
- Item_allany_subselect *it=
- new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
- if (all)
- return it->upper_item= new Item_func_not_all(it); /* ALL */
- return it->upper_item= new Item_func_nop_all(it); /* ANY/SOME */
- }
- /*
- CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
- the proper arguments. This isn't very fast but it should work for most
- cases.
- In the future ALTER TABLE will notice that only added indexes
- and create these one by one for the existing table without having to do
- a full rebuild.
- One should normally create all indexes with CREATE TABLE or ALTER TABLE.
- */
- int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
- {
- List<create_field> fields;
- ALTER_INFO alter_info;
- alter_info.flags= ALTER_ADD_INDEX;
- alter_info.is_simple= 0;
- HA_CREATE_INFO create_info;
- DBUG_ENTER("mysql_create_index");
- bzero((char*) &create_info,sizeof(create_info));
- create_info.db_type=DB_TYPE_DEFAULT;
- create_info.default_table_charset= thd->variables.collation_database;
- DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
- &create_info, table_list,
- fields, keys, 0, (ORDER*)0,
- DUP_ERROR, 0, &alter_info));
- }
- int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
- {
- List<create_field> fields;
- List<Key> keys;
- HA_CREATE_INFO create_info;
- DBUG_ENTER("mysql_drop_index");
- bzero((char*) &create_info,sizeof(create_info));
- create_info.db_type=DB_TYPE_DEFAULT;
- create_info.default_table_charset= thd->variables.collation_database;
- alter_info->clear();
- alter_info->flags= ALTER_DROP_INDEX;
- alter_info->is_simple= 0;
- DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
- &create_info, table_list,
- fields, keys, 0, (ORDER*)0,
- DUP_ERROR, 0, alter_info));
- }
- /*
- Multi update query pre-check
- SYNOPSIS
- multi_update_precheck()
- thd Thread handler
- tables Global table list
- RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
- -1 Error (message is not sent to user)
- */
- int multi_update_precheck(THD *thd, TABLE_LIST *tables)
- {
- DBUG_ENTER("multi_update_precheck");
- const char *msg= 0;
- TABLE_LIST *table;
- LEX *lex= thd->lex;
- SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first;
- if (select_lex->item_list.elements != lex->value_list.elements)
- {
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
- }
- /*
- Ensure that we have UPDATE or SELECT privilege for each table
- The exact privilege is checked in mysql_multi_update()
- */
- for (table= update_list; table; table= table->next)
- {
- 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);
- /*
- We assign following flag only to copy of table, because it will
- be checked only if query contains subqueries i.e. only if copy exists
- */
- if (table->table_list)
- table->table_list->table_in_update_from_clause= 1;
- }
- /*
- Is there tables of subqueries?
- */
- if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
- {
- DBUG_PRINT("info",("Checking sub query list"));
- for (table= tables; table; table= table->next)
- {
- if (my_tz_check_n_skip_implicit_tables(&table,
- lex->time_zone_tables_used))
- continue;
- else if (table->table_in_update_from_clause)
- {
- /*
- If we check table by local TABLE_LIST copy then we should copy
- grants to global table list, because it will be used for table
- opening.
- */
- if (table->table_list)
- table->grant= table->table_list->grant;
- }
- else if (!table->derived)
- {
- if (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);
- }
- }
- }
- if (select_lex->order_list.elements)
- msg= "ORDER BY";
- else if (select_lex->select_limit && select_lex->select_limit !=
- HA_POS_ERROR)
- msg= "LIMIT";
- if (msg)
- {
- my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
- /*
- Multi delete query pre-check
- SYNOPSIS
- multi_delete_precheck()
- thd Thread handler
- tables Global table list
- table_count Pointer to table counter
- RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
- */
- int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
- {
- DBUG_ENTER("multi_delete_precheck");
- SELECT_LEX *select_lex= &thd->lex->select_lex;
- TABLE_LIST *aux_tables=
- (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
- TABLE_LIST *target_tbl;
- *table_count= 0;
- /* sql_yacc guarantees that tables and aux_tables are not zero */
- DBUG_ASSERT(aux_tables != 0);
- if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
- check_table_access(thd,SELECT_ACL, tables,0) ||
- check_table_access(thd,DELETE_ACL, aux_tables,0))
- DBUG_RETURN(1);
- if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
- {
- my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
- DBUG_RETURN(-1);
- }
- for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
- {
- (*table_count)++;
- /* All tables in aux_tables must be found in FROM PART */
- TABLE_LIST *walk;
- walk= get_table_by_alias(delete_tables,target_tbl->db,target_tbl->alias);
- if (!walk)
- {
- my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
- "MULTI DELETE");
- DBUG_RETURN(-1);
- }
- if (walk->derived)
- {
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
- "DELETE");
- DBUG_RETURN(-1);
- }
- walk->lock_type= target_tbl->lock_type;
- target_tbl->table_list= walk; // Remember corresponding table
-
- /* in case of subselects, we need to set lock_type in
- * corresponding table in list of all tables */
- if (walk->table_list)
- {
- target_tbl->table_list= walk->table_list;
- walk->table_list->lock_type= walk->lock_type;
- }
- }
- DBUG_RETURN(0);
- }
- /*
- simple UPDATE query pre-check
- SYNOPSIS
- update_precheck()
- thd Thread handler
- tables Global table list
- RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
- -1 Error (message is not sent to user)
- */
- int update_precheck(THD *thd, TABLE_LIST *tables)
- {
- DBUG_ENTER("update_precheck");
- if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
- {
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
- }
- DBUG_RETURN((check_db_used(thd, tables) ||
- check_one_table_access(thd, UPDATE_ACL, tables)) ? 1 : 0);
- }
- /*
- simple DELETE query pre-check
- SYNOPSIS
- delete_precheck()
- thd Thread handler
- tables Global table list
- RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
- */
- int delete_precheck(THD *thd, TABLE_LIST *tables)
- {
- DBUG_ENTER("delete_precheck");
- if (check_one_table_access(thd, DELETE_ACL, tables))
- DBUG_RETURN(1);
- /* Set privilege for the WHERE clause */
- tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
- DBUG_RETURN(0);
- }
- /*
- simple INSERT query pre-check
- SYNOPSIS
- insert_precheck()
- thd Thread handler
- tables Global table list
- RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
- */
- int insert_precheck(THD *thd, TABLE_LIST *tables)
- {
- LEX *lex= thd->lex;
- DBUG_ENTER("insert_precheck");
- /*
- Check that we have modify privileges for the first table and
- select privileges for the rest
- */
- 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);
- if (lex->update_list.elements != lex->value_list.elements)
- {
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
- /*
- CREATE TABLE query pre-check
- SYNOPSIS
- create_table_precheck()
- thd Thread handler
- tables Global table list
- create_table Table which will be created
- RETURN VALUE
- 0 OK
- 1 Error (message is 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");
- 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))
- 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 */
- /*
- For temporary tables or PREPARED STATEMETNS we don't have to check
- if the created table exists
- */
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- ! thd->current_arena->is_stmt_prepare() &&
- 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);
- }
- /*
- negate given expression
- SYNOPSIS
- negate_expression()
- thd therad handler
- expr expression for negation
- RETURN
- negated expression
- */
- Item *negate_expression(THD *thd, Item *expr)
- {
- Item *negated;
- if (expr->type() == Item::FUNC_ITEM &&
- ((Item_func *) expr)->functype() == Item_func::NOT_FUNC)
- {
- /* it is NOT(NOT( ... )) */
- Item *arg= ((Item_func *) expr)->arguments()[0];
- enum_parsing_place place= thd->lex->current_select->parsing_place;
- if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING)
- return arg;
- /*
- if it is not boolean function then we have to emulate value of
- not(not(a)), it will be a != 0
- */
- return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1));
- }
- if ((negated= expr->neg_transformer(thd)) != 0)
- return negated;
- return new Item_func_not(expr);
- }