sql_base.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:83k
- /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
- /* Basic functions needed by many modules */
- #include "mysql_priv.h"
- #include "sql_select.h"
- #include <m_ctype.h>
- #include <my_dir.h>
- #include <hash.h>
- #include <nisam.h>
- #ifdef __WIN__
- #include <io.h>
- #endif
- TABLE *unused_tables; /* Used by mysql_test */
- HASH open_cache; /* Used by mysql_test */
- HASH assign_cache;
- static int open_unireg_entry(THD *thd,TABLE *entry,const char *db,
- const char *name, const char *alias);
- static void free_cache_entry(TABLE *entry);
- static void mysql_rm_tmp_tables(void);
- extern "C" byte *table_cache_key(const byte *record,uint *length,
- my_bool not_used __attribute__((unused)))
- {
- TABLE *entry=(TABLE*) record;
- *length=entry->key_length;
- return (byte*) entry->table_cache_key;
- }
- bool table_cache_init(void)
- {
- mysql_rm_tmp_tables();
- return hash_init(&open_cache, &my_charset_bin, table_cache_size+16,
- 0, 0,table_cache_key,
- (hash_free_key) free_cache_entry, 0) != 0;
- }
- void table_cache_free(void)
- {
- DBUG_ENTER("table_cache_free");
- close_cached_tables((THD*) 0,0,(TABLE_LIST*) 0);
- if (!open_cache.records) // Safety first
- hash_free(&open_cache);
- DBUG_VOID_RETURN;
- }
- uint cached_tables(void)
- {
- return open_cache.records;
- }
- #ifdef EXTRA_DEBUG
- static void check_unused(void)
- {
- uint count=0,idx=0;
- TABLE *cur_link,*start_link;
- if ((start_link=cur_link=unused_tables))
- {
- do
- {
- if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
- {
- DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
- return; /* purecov: inspected */
- }
- } while (count++ < open_cache.records &&
- (cur_link=cur_link->next) != start_link);
- if (cur_link != start_link)
- {
- DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
- }
- }
- for (idx=0 ; idx < open_cache.records ; idx++)
- {
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- if (!entry->in_use)
- count--;
- }
- if (count != 0)
- {
- DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
- count)); /* purecov: inspected */
- }
- }
- #else
- #define check_unused()
- #endif
- /*
- Create a list for all open tables matching SQL expression
- SYNOPSIS
- list_open_tables()
- thd Thread THD
- wild SQL like expression
- NOTES
- One gets only a list of tables for which one has any kind of privilege.
- db and table names are allocated in result struct, so one doesn't need
- a lock on LOCK_open when traversing the return list.
- RETURN VALUES
- NULL Error (Probably OOM)
- # Pointer to list of names of open tables.
- */
- OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild)
- {
- int result = 0;
- OPEN_TABLE_LIST **start_list, *open_list;
- TABLE_LIST table_list;
- char name[NAME_LEN*2];
- DBUG_ENTER("list_open_tables");
- VOID(pthread_mutex_lock(&LOCK_open));
- bzero((char*) &table_list,sizeof(table_list));
- start_list= &open_list;
- open_list=0;
- for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++)
- {
- OPEN_TABLE_LIST *table;
- TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
- DBUG_ASSERT(entry->real_name);
- if ((!entry->real_name)) // To be removed
- continue; // Shouldn't happen
- if (wild)
- {
- strxmov(name,entry->table_cache_key,".",entry->real_name,NullS);
- if (wild_compare(name,wild,0))
- continue;
- }
- /* Check if user has SELECT privilege for any column in the table */
- table_list.db= (char*) entry->table_cache_key;
- table_list.real_name= entry->real_name;
- table_list.grant.privilege=0;
- if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1))
- continue;
- /* need to check if we haven't already listed it */
- for (table= open_list ; table ; table=table->next)
- {
- if (!strcmp(table->table,entry->real_name) &&
- !strcmp(table->db,entry->table_cache_key))
- {
- if (entry->in_use)
- table->in_use++;
- if (entry->locked_by_name)
- table->locked++;
- break;
- }
- }
- if (table)
- continue;
- if (!(*start_list = (OPEN_TABLE_LIST *)
- sql_alloc(sizeof(**start_list)+entry->key_length)))
- {
- open_list=0; // Out of memory
- break;
- }
- strmov((*start_list)->table=
- strmov(((*start_list)->db= (char*) ((*start_list)+1)),
- entry->table_cache_key)+1,
- entry->real_name);
- (*start_list)->in_use= entry->in_use ? 1 : 0;
- (*start_list)->locked= entry->locked_by_name ? 1 : 0;
- start_list= &(*start_list)->next;
- *start_list=0;
- }
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(open_list);
- }
- /*****************************************************************************
- * Functions to free open table cache
- ****************************************************************************/
- void intern_close_table(TABLE *table)
- { // Free all structures
- free_io_cache(table);
- if (table->file)
- VOID(closefrm(table)); // close file
- }
- /*
- Remove table from the open table cache
- SYNOPSIS
- free_cache_entry()
- table Table to remove
- NOTE
- We need to have a lock on LOCK_open when calling this
- */
- static void free_cache_entry(TABLE *table)
- {
- DBUG_ENTER("free_cache_entry");
- safe_mutex_assert_owner(&LOCK_open);
- intern_close_table(table);
- if (!table->in_use)
- {
- table->next->prev=table->prev; /* remove from used chain */
- table->prev->next=table->next;
- if (table == unused_tables)
- {
- unused_tables=unused_tables->next;
- if (table == unused_tables)
- unused_tables=0;
- }
- check_unused(); // consisty check
- }
- my_free((gptr) table,MYF(0));
- DBUG_VOID_RETURN;
- }
- /* Free resources allocated by filesort() and read_record() */
- void free_io_cache(TABLE *table)
- {
- DBUG_ENTER("free_io_cache");
- if (table->sort.io_cache)
- {
- close_cached_file(table->sort.io_cache);
- my_free((gptr) table->sort.io_cache,MYF(0));
- table->sort.io_cache=0;
- }
- DBUG_VOID_RETURN;
- }
- /*
- Close all tables which aren't in use by any thread
- THD can be NULL, but then if_wait_for_refresh must be FALSE
- and tables must be NULL.
- */
- bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
- TABLE_LIST *tables)
- {
- bool result=0;
- DBUG_ENTER("close_cached_tables");
- DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables));
- VOID(pthread_mutex_lock(&LOCK_open));
- if (!tables)
- {
- while (unused_tables)
- {
- #ifdef EXTRA_DEBUG
- if (hash_delete(&open_cache,(byte*) unused_tables))
- printf("Warning: Couldn't delete open table from hashn");
- #else
- VOID(hash_delete(&open_cache,(byte*) unused_tables));
- #endif
- }
- refresh_version++; // Force close of open tables
- }
- else
- {
- bool found=0;
- for (TABLE_LIST *table=tables ; table ; table=table->next)
- {
- if (remove_table_from_cache(thd, table->db, table->real_name,
- RTFC_OWNED_BY_THD_FLAG))
- found=1;
- }
- if (!found)
- if_wait_for_refresh=0; // Nothing to wait for
- }
- #ifndef EMBEDDED_LIBRARY
- if (!tables)
- kill_delayed_threads();
- #endif
- if (if_wait_for_refresh)
- {
- /*
- If there is any table that has a lower refresh_version, wait until
- this is closed (or this thread is killed) before returning
- */
- thd->mysys_var->current_mutex= &LOCK_open;
- thd->mysys_var->current_cond= &COND_refresh;
- thd->proc_info="Flushing tables";
- close_old_data_files(thd,thd->open_tables,1,1);
- mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
- TRUE);
- bool found=1;
- /* Wait until all threads has closed all the tables we had locked */
- DBUG_PRINT("info",
- ("Waiting for others threads to close their open tables"));
- while (found && ! thd->killed)
- {
- found=0;
- for (uint idx=0 ; idx < open_cache.records ; idx++)
- {
- TABLE *table=(TABLE*) hash_element(&open_cache,idx);
- if ((table->version) < refresh_version && table->db_stat)
- {
- found=1;
- pthread_cond_wait(&COND_refresh,&LOCK_open);
- break;
- }
- }
- }
- /*
- No other thread has the locked tables open; reopen them and get the
- old locks. This should always succeed (unless some external process
- has removed the tables)
- */
- thd->in_lock_tables=1;
- result=reopen_tables(thd,1,1);
- thd->in_lock_tables=0;
- /* Set version for table */
- for (TABLE *table=thd->open_tables; table ; table=table->next)
- table->version=refresh_version;
- }
- VOID(pthread_mutex_unlock(&LOCK_open));
- if (if_wait_for_refresh)
- {
- pthread_mutex_lock(&thd->mysys_var->mutex);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- thd->proc_info=0;
- pthread_mutex_unlock(&thd->mysys_var->mutex);
- }
- DBUG_RETURN(result);
- }
- /*
- Close all tables used by thread
- SYNOPSIS
- close_thread_tables()
- thd Thread handler
- lock_in_use Set to 1 (0 = default) if caller has a lock on
- LOCK_open
- skip_derived Set to 1 (0 = default) if we should not free derived
- tables.
- IMPLEMENTATION
- Unlocks tables and frees derived tables.
- Put all normal tables used by thread in free list.
- */
- void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
- {
- bool found_old_table;
- DBUG_ENTER("close_thread_tables");
- if (thd->derived_tables && !skip_derived)
- {
- TABLE *table, *next;
- /*
- Close all derived tables generated from questions like
- SELECT * from (select * from t1))
- */
- for (table= thd->derived_tables ; table ; table= next)
- {
- next= table->next;
- free_tmp_table(thd, table);
- }
- thd->derived_tables= 0;
- }
- if (thd->locked_tables)
- {
- ha_commit_stmt(thd); // If select statement
- DBUG_VOID_RETURN; // LOCK TABLES in use
- }
- if (thd->lock)
- {
- mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
- }
- /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
- if (!lock_in_use)
- VOID(pthread_mutex_lock(&LOCK_open));
- safe_mutex_assert_owner(&LOCK_open);
- DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables));
- found_old_table= 0;
- while (thd->open_tables)
- found_old_table|=close_thread_table(thd, &thd->open_tables);
- thd->some_tables_deleted=0;
- /* Free tables to hold down open files */
- while (open_cache.records > table_cache_size && unused_tables)
- VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
- check_unused();
- if (found_old_table)
- {
- /* Tell threads waiting for refresh that something has happened */
- VOID(pthread_cond_broadcast(&COND_refresh));
- }
- if (!lock_in_use)
- VOID(pthread_mutex_unlock(&LOCK_open));
- /* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
- DBUG_VOID_RETURN;
- }
- /* move one table to free list */
- bool close_thread_table(THD *thd, TABLE **table_ptr)
- {
- DBUG_ENTER("close_thread_table");
- bool found_old_table= 0;
- TABLE *table= *table_ptr;
- DBUG_ASSERT(table->key_read == 0);
- *table_ptr=table->next;
- if (table->version != refresh_version ||
- thd->version != refresh_version || !table->db_stat)
- {
- VOID(hash_delete(&open_cache,(byte*) table));
- found_old_table=1;
- }
- else
- {
- if (table->flush_version != flush_version)
- {
- table->flush_version=flush_version;
- table->file->extra(HA_EXTRA_FLUSH);
- }
- else
- {
- // Free memory and reset for next loop
- table->file->reset();
- }
- table->in_use=0;
- if (unused_tables)
- {
- table->next=unused_tables; /* Link in last */
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
- }
- DBUG_RETURN(found_old_table);
- }
- /* Close and delete temporary tables */
- void close_temporary(TABLE *table,bool delete_table)
- {
- DBUG_ENTER("close_temporary");
- char path[FN_REFLEN];
- db_type table_type=table->db_type;
- strmov(path,table->path);
- free_io_cache(table);
- closefrm(table);
- my_free((char*) table,MYF(0));
- if (delete_table)
- rm_temporary_table(table_type, path);
- DBUG_VOID_RETURN;
- }
- void close_temporary_tables(THD *thd)
- {
- TABLE *table,*next;
- char *query, *end;
- uint query_buf_size;
- bool found_user_tables = 0;
- if (!thd->temporary_tables)
- return;
-
- LINT_INIT(end);
- query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS
- for (table=thd->temporary_tables ; table ; table=table->next)
- /*
- We are going to add 4 ` around the db/table names, so 1 does not look
- enough; indeed it is enough, because table->key_length is greater (by 8,
- because of server_id and thread_id) than db||table.
- */
- query_buf_size+= table->key_length+1;
- if ((query = alloc_root(thd->mem_root, query_buf_size)))
- // Better add "if exists", in case a RESET MASTER has been done
- end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
- for (table=thd->temporary_tables ; table ; table=next)
- {
- if (query) // we might be out of memory, but this is not fatal
- {
- // skip temporary tables not created directly by the user
- if (table->real_name[0] != '#')
- found_user_tables = 1;
- /*
- Here we assume table_cache_key always starts
- with