2001-12-22 14:13:31 +01:00
/* Copyright (C) 2000 MySQL AB
2001-12-02 13:34:01 +01:00
2000-07-31 21:29:14 +02:00
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 .
2001-12-02 13:34:01 +01:00
2000-07-31 21:29:14 +02:00
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 .
2001-12-02 13:34:01 +01:00
2000-07-31 21:29:14 +02:00
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 */
2001-12-05 12:03:00 +01:00
/*
Description of the query cache :
1. Query_cache object consists of
- query cache memory pool ( cache )
- queries hash ( queries )
- tables hash ( tables )
- list of blocks ordered as they allocated in memory
( first_block )
- list of queries block ( queries_blocks )
- list of used tables ( tables_blocks )
2. Query cache memory pool ( cache ) consists of
- table of steps of memory bins allocation
- table of free memory bins
- blocks of memory
3. Memory blocks
Every memory block has the following structure :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Block header ( Query_cache_block structure ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Table of database table lists ( used for queries & tables ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Type depended header |
| ( Query_cache_query , Query_cache_table , Query_cache_result ) |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Data . . . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
Block header consists of :
- type :
FREE Free memory block
QUERY Query block
RESULT Ready to send result
RES_CONT Result ' s continuation
RES_BEG First block of results , that is not yet complete ,
written to cache
RES_INCOMPLETE Allocated for results data block
TABLE Block with database table description
INCOMPLETE The destroyed block
- length of block ( length )
- length of data & headers ( used )
- physical list links ( pnext / pprev ) - used for the list of
blocks ordered as they are allocated in physical memory
- logical list links ( next / prev ) - used for queries block list , tables block
list , free memory block lists and list of results block in query
- number of elements in table of database table list ( n_tables )
4. Query & results blocks
Query stored in cache consists of following blocks :
more more
recent + - - - - - - - - - - - - - + old
< - - - - - | Query block 1 | - - - - - - > double linked list of queries block
prev | | next
+ - - - - - - - - - - - - - +
< - | table 0 | - > ( see " Table of database table lists " description )
< - | table 1 | - >
| . . . | + - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + |
NET | | | V V |
struct | | + - + - - - - - - - - - - - - + + - - - - - - - - - - - - + |
< - - - - - | query header | - - - - - > | Result block | - - > | Result block | - + doublelinked
writer | | result | | < - - | | list of results
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - + + - - - - - - - - - - - - +
| charset | + - - - - - - - - - - - - + + - - - - - - - - - - - - + no table of dbtables
| encoding + | | result | | result |
| query text | < - - - - - | header | | header | - - - - - - +
+ - - - - - - - - - - - - - + parent | | | | parent |
^ + - - - - - - - - - - - - + + - - - - - - - - - - - - + |
| | result data | | result data | |
| + - - - - - - - - - - - - + + - - - - - - - - - - - - + |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
First query is registered . During the registration query block is
allocated . This query block is included in query hash and is linked
with appropriate database tables lists ( if there is no appropriate
list exists it will be created ) .
Later when query has performed results is written into the result blocks .
A result block cannot be smaller then QUERY_CACHE_MIN_RESULT_DATA_SIZE .
When new result is written to cache it is appended to the last result
block , if no more free space left in the last block , new block is
allocated .
5. Table of database table lists .
For quick invalidation of queries all query are linked in lists on used
database tables basis ( when table will be changed ( insert / delete / . . . )
this queries will be removed from cache ) .
Root of such list is table block :
+ - - - - - - - - - - - - + list of used tables ( used while invalidation of
< - - - - | Table | - - - - - > whole database )
prev | block | next + - - - - - - - - - - - +
| | + - - - - - - - - - - - + | Query block |
| | | Query block | + - - - - - - - - - - - +
+ - - - - - - - - - - - - + + - - - - - - - - - - - + | . . . |
+ - > | table 0 | - - - - - - > | table 0 | - - - - - > | table N | - - - +
| + - | | < - - - - - - | | < - - - - - | | < - + |
| | + - - - - - - - - - - - - + | . . . | | . . . | | |
| | | table header | + - - - - - - - - - - - + + - - - - - - - - - - - + | |
| | + - - - - - - - - - - - - + | . . . | | . . . | | |
| | | db name + | + - - - - - - - - - - - + + - - - - - - - - - - - + | |
| | | table name | | |
| | + - - - - - - - - - - - - + | |
| + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
Table block is included into the tables hash ( tables ) .
6. Free blocks , free blocks bins & steps of freeblock bins .
When we just started only one free memory block existed . All query
cache memory ( that will be used for block allocation ) were
containing in this block .
When a new block is allocated we find most suitable memory block
( minimal of > = required size ) . If such a block can not be found , we try
to find max block < required size ( if we allocate block for results ) .
If there is no free memory , oldest query is removed from cache , and then
we try to allocate memory . Last step should be repeated until we find
suitable block or until there is no unlocked query found .
If the block is found and its length more then we need , it should be
split into 2 blocks .
New blocks cannot be smaller then min_allocation_unit_bytes .
When a block becomes free , its neighbor - blocks should be tested and if
there are free blocks among them , they should be joined into one block .
Free memory blocks are stored in bins according to their sizes .
The bins are stored in size - descending order .
These bins are distributed ( by size ) approximately logarithmically .
First bin ( number 0 ) stores free blocks with
size < = query_cache_size > > QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 .
It is first ( number 0 ) step .
On the next step distributed ( 1 + QUERY_CACHE_MEM_BIN_PARTS_INC ) *
QUERY_CACHE_MEM_BIN_PARTS_MUL bins . This bins allocated in interval from
query_cache_size > > QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 to
query_cache_size > > QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 > >
QUERY_CACHE_MEM_BIN_STEP_PWR2
. . .
On each step interval decreases in 2 power of
QUERY_CACHE_MEM_BIN_STEP_PWR2
times , number of bins ( that distributed on this step ) increases . If on
the previous step there were N bins distributed , on the current there
would be distributed
( N + QUERY_CACHE_MEM_BIN_PARTS_INC ) * QUERY_CACHE_MEM_BIN_PARTS_MUL
bins .
Last distributed bin stores blocks with size near min_allocation_unit
bytes .
For example :
query_cache_size > > QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 = 100 ,
min_allocation_unit = 17 ,
QUERY_CACHE_MEM_BIN_STEP_PWR2 = 1 ,
QUERY_CACHE_MEM_BIN_PARTS_INC = 1 ,
QUERY_CACHE_MEM_BIN_PARTS_MUL = 1
( in followed picture showed right ( low ) bound of bin ) :
| 100 > > 1 50 > > 1 | 25 > > 1 |
| | | | | |
| 100 75 50 41 33 25 21 18 15 | 12 | - bins right ( low ) bounds
| \ - - - / \ - - - - - / \ - - - - - - - - / \ - - - - - - - - | - - - / |
| 0 1 2 3 | | - steps
\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - / \ - - - /
bins that we store in cache this bin showed for example only
Calculation of steps / bins distribution is performed only when query cache
is resized .
When we need to find appropriate bin , first we should find appropriate
step , then we should calculate number of bins that are using data
stored in Query_cache_memory_bin_step structure .
Free memory blocks are sorted in bins in lists with size - ascending order
( more small blocks needed frequently then bigger one ) .
2001-12-06 00:05:30 +01:00
2001-12-13 01:31:19 +01:00
7. Packing cache .
2001-12-06 00:05:30 +01:00
Query cache packing is divided into two operation :
- pack_cache
- join_results
pack_cache moved all blocks to " top " of cache and create one block of free
space at the " bottom " :
before pack_cache after pack_cache
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| query 1 | | query 1 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| table 1 | | table 1 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| results 1.1 | | results 1.1 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| free | | query 2 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| query 2 | | table 2 |
+ - - - - - - - - - - - - - + - - - > + - - - - - - - - - - - - - +
| table 2 | | results 1.2 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| results 1.2 | | results 2 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| free | | free |
+ - - - - - - - - - - - - - + | |
| results 2 | | |
+ - - - - - - - - - - - - - + | |
| free | | |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
pack_cache scan blocks in physical address order and move every non - free
block " higher " .
pack_cach remove every free block it finds . The length of the deleted block
is accumulated to the " gap " . All non free blocks should be shifted with the
" gap " step .
join_results scans all complete queries . If the results of query are not
stored in the same block , join_results tries to move results so , that they
are stored in one block .
before join_results after join_results
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| query 1 | | query 1 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| table 1 | | table 1 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| results 1.1 | | free |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| query 2 | | query 2 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| table 2 | | table 2 |
+ - - - - - - - - - - - - - + - - - > + - - - - - - - - - - - - - +
| results 1.2 | | free |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| results 2 | | results 2 |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
| free | | results 1 |
| | | |
| | + - - - - - - - - - - - - - +
| | | free |
| | | |
+ - - - - - - - - - - - - - + + - - - - - - - - - - - - - +
If join_results allocated new block ( s ) then we need call pack_cache again .
2002-01-08 07:38:33 +01:00
TODO list :
- Invalidate queries that use innoDB tables changed in transaction & remove
invalidation by table type
- Delayed till after - parsing qache answer ( for column rights processing )
- Optimize cache resizing
- if new_size < old_size then pack & shrink
- if new_size > old_size copy cached query to new cache
- Move MRG_MYISAM table type processing to handlers , something like :
tables_used - > table - > file - > register_used_filenames ( callback ,
first_argument ) ;
- In Query_cache : : insert_table eliminate strlen ( ) . To do this we have to
add db_len to the TABLE_LIST and TABLE structures .
2001-12-05 12:03:00 +01:00
*/
2000-07-31 21:29:14 +02:00
# include "mysql_priv.h"
# include <m_ctype.h>
# include <my_dir.h>
# include <hash.h>
2001-12-02 13:34:01 +01:00
# include "sql_acl.h"
# include "ha_myisammrg.h"
# ifndef MASTER
# include "../srclib/myisammrg/myrg_def.h"
# else
# include "../myisammrg/myrg_def.h"
# endif
2001-12-05 12:03:00 +01:00
# include <assert.h>
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
# if defined(EXTRA_DEBUG) && !defined(DBUG_OFF)
# define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \
2001-12-02 13:34:01 +01:00
pthread_mutex_lock ( M ) ; }
2001-12-05 12:03:00 +01:00
# define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
2001-12-02 13:34:01 +01:00
( ulong ) ( M ) ) ) ; pthread_mutex_unlock ( M ) ; }
2002-01-02 20:29:41 +01:00
# define SEM_LOCK(M) { int val = 0; sem_getvalue (M, &val); \
DBUG_PRINT ( " lock " , ( " sem lock 0x%lx (%d) " , ( ulong ) ( M ) , val ) ) ; \
sem_wait ( M ) ; DBUG_PRINT ( " lock " , ( " sem lock ok " ) ) ; }
# define SEM_UNLOCK(M) {DBUG_PRINT("info", ("sem unlock 0x%lx", (ulong)(M))); \
sem_post ( M ) ; DBUG_PRINT ( " info " , ( " sem unlock ok " ) ) ; }
2001-12-05 12:03:00 +01:00
# define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
pthread_mutex_lock ( M ) ; DBUG_PRINT ( " lock " , ( " struct lock OK " ) ) ; }
2001-12-02 13:34:01 +01:00
# define STRUCT_UNLOCK(M) { \
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " lock " , ( " %d struct unlock... " , __LINE__ ) ) ; \
pthread_mutex_unlock ( M ) ; DBUG_PRINT ( " lock " , ( " struct unlock OK " ) ) ; }
# define BLOCK_LOCK_WR(B) {DBUG_PRINT("lock", ("%d LOCK_WR 0x%lx",\
2001-12-02 13:34:01 +01:00
__LINE__ , ( ulong ) ( B ) ) ) ; \
B - > query ( ) - > lock_writing ( ) ; }
2001-12-05 12:03:00 +01:00
# define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\
2001-12-02 13:34:01 +01:00
__LINE__ , ( ulong ) ( B ) ) ) ; \
B - > query ( ) - > lock_reading ( ) ; }
# define BLOCK_UNLOCK_WR(B) { \
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " lock " , ( " %d UNLOCK_WR 0x%lx " , \
2001-12-02 13:34:01 +01:00
__LINE__ , ( ulong ) ( B ) ) ) ; B - > query ( ) - > unlock_writing ( ) ; }
# define BLOCK_UNLOCK_RD(B) { \
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " lock " , ( " %d UNLOCK_RD 0x%lx " , \
2001-12-02 13:34:01 +01:00
__LINE__ , ( ulong ) ( B ) ) ) ; B - > query ( ) - > unlock_reading ( ) ; }
2001-12-13 01:31:19 +01:00
# define DUMP(C) DBUG_EXECUTE("qcache", {\
( C ) - > cache_dump ( ) ; ( C ) - > queries_dump ( ) ; ( C ) - > tables_dump ( ) ; } )
2001-12-02 13:34:01 +01:00
# else
# define MUTEX_LOCK(M) pthread_mutex_lock(M)
# define MUTEX_UNLOCK(M) pthread_mutex_unlock(M)
2002-01-02 20:29:41 +01:00
# define SEM_LOCK(M) sem_wait(M)
# define SEM_UNLOCK(M) sem_post(M)
2001-12-02 13:34:01 +01:00
# define STRUCT_LOCK(M) pthread_mutex_lock(M)
# define STRUCT_UNLOCK(M) pthread_mutex_unlock(M)
# define BLOCK_LOCK_WR(B) B->query()->lock_writing()
# define BLOCK_LOCK_RD(B) B->query()->lock_reading()
# define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing()
# define BLOCK_UNLOCK_RD(B) B->query()->unlock_reading()
# define DUMP(C)
# endif
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query_cache_block_table method ( s )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
inline Query_cache_block * Query_cache_block_table : : block ( )
{
2001-12-05 12:03:00 +01:00
return ( Query_cache_block * ) ( ( ( byte * ) this ) -
2001-12-09 23:08:24 +01:00
ALIGN_SIZE ( sizeof ( Query_cache_block_table ) * n ) -
2001-12-05 12:03:00 +01:00
ALIGN_SIZE ( sizeof ( Query_cache_block ) ) ) ;
2001-12-02 13:34:01 +01:00
} ;
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query_cache_block method ( s )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
void Query_cache_block : : init ( ulong block_length )
{
DBUG_ENTER ( " Query_cache_block::init " ) ;
2002-01-03 18:04:01 +01:00
DBUG_PRINT ( " qcache " , ( " init block 0x%lx length: %lu " , ( ulong ) this ,
block_length ) ) ;
2001-12-02 13:34:01 +01:00
length = block_length ;
used = 0 ;
type = Query_cache_block : : FREE ;
n_tables = 0 ;
DBUG_VOID_RETURN ;
}
void Query_cache_block : : destroy ( )
{
DBUG_ENTER ( " Query_cache_block::destroy " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " destroy block 0x%lx, type %d " ,
( ulong ) this , type ) ) ;
2001-12-02 13:34:01 +01:00
type = INCOMPLETE ;
DBUG_VOID_RETURN ;
}
inline uint Query_cache_block : : headers_len ( )
{
2001-12-05 12:03:00 +01:00
return ( ALIGN_SIZE ( sizeof ( Query_cache_block_table ) * n_tables ) +
2001-12-02 13:34:01 +01:00
ALIGN_SIZE ( sizeof ( Query_cache_block ) ) ) ;
}
inline gptr Query_cache_block : : data ( void )
{
return ( gptr ) ( ( ( byte * ) this ) + headers_len ( ) ) ;
}
inline Query_cache_query * Query_cache_block : : query ( )
{
# ifndef DBUG_OFF
if ( type ! = QUERY )
query_cache . wreck ( __LINE__ , " incorrect block type " ) ;
# endif
return ( Query_cache_query * ) data ( ) ;
}
inline Query_cache_table * Query_cache_block : : table ( )
{
# ifndef DBUG_OFF
if ( type ! = TABLE )
query_cache . wreck ( __LINE__ , " incorrect block type " ) ;
# endif
return ( Query_cache_table * ) data ( ) ;
}
inline Query_cache_result * Query_cache_block : : result ( )
{
# ifndef DBUG_OFF
if ( type ! = RESULT & & type ! = RES_CONT & & type ! = RES_BEG & &
type ! = RES_INCOMPLETE )
query_cache . wreck ( __LINE__ , " incorrect block type " ) ;
# endif
return ( Query_cache_result * ) data ( ) ;
}
inline Query_cache_block_table * Query_cache_block : : table ( TABLE_COUNTER_TYPE n )
{
return ( ( Query_cache_block_table * )
( ( ( byte * ) this ) + ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
2001-12-05 12:03:00 +01:00
n * sizeof ( Query_cache_block_table ) ) ) ;
2001-12-02 13:34:01 +01:00
}
/*****************************************************************************
* Query_cache_table method ( s )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-05 12:03:00 +01:00
extern " C "
{
byte * query_cache_table_get_key ( const byte * record , uint * length ,
my_bool not_used __attribute__ ( ( unused ) ) )
2001-12-02 13:34:01 +01:00
{
Query_cache_block * table_block = ( Query_cache_block * ) record ;
* length = ( table_block - > used - table_block - > headers_len ( ) -
ALIGN_SIZE ( sizeof ( Query_cache_table ) ) ) ;
return ( ( ( byte * ) table_block - > data ( ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_table ) ) ) ;
}
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query_cache_query methods
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
void Query_cache_query : : init_n_lock ( )
{
DBUG_ENTER ( " Query_cache_query::init_n_lock " ) ;
res = 0 ; wri = 0 ; len = 0 ;
2002-01-02 20:29:41 +01:00
sem_init ( & lock , 0 , 1 ) ;
2001-12-02 13:34:01 +01:00
pthread_mutex_init ( & clients_guard , MY_MUTEX_INIT_FAST ) ;
clients = 0 ;
lock_writing ( ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " inited & locked query for block 0x%lx " ,
( ( byte * ) this ) - ALIGN_SIZE ( sizeof ( Query_cache_block ) ) ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache_query : : unlock_n_destroy ( )
{
DBUG_ENTER ( " Query_cache_query::unlock_n_destroy " ) ;
2001-12-09 23:08:24 +01:00
DBUG_PRINT ( " qcache " , ( " destroyed & unlocked query for block 0x%lx " ,
( ( byte * ) this ) - ALIGN_SIZE ( sizeof ( Query_cache_block ) ) ) ) ;
2001-12-06 00:05:30 +01:00
/*
The following call is not needed on system where one can destroy an
active semaphore
*/
2001-12-02 13:34:01 +01:00
this - > unlock_writing ( ) ;
2002-01-02 20:29:41 +01:00
sem_destroy ( & lock ) ;
2001-12-02 13:34:01 +01:00
pthread_mutex_destroy ( & clients_guard ) ;
DBUG_VOID_RETURN ;
}
/*
2001-12-05 12:03:00 +01:00
Following methods work for block read / write locking only in this
2001-12-02 13:34:01 +01:00
particular case and in interaction with structure_guard_mutex .
2001-12-05 12:03:00 +01:00
Lock for write prevents any other locking . ( exclusive use )
2001-12-02 13:34:01 +01:00
Lock for read prevents only locking for write .
*/
void Query_cache_query : : lock_writing ( )
{
2002-01-02 20:29:41 +01:00
SEM_LOCK ( & lock ) ;
2001-12-02 13:34:01 +01:00
}
/*
Needed for finding queries , that we may delete from cache .
2001-12-05 12:03:00 +01:00
We don ' t want to wait while block become unlocked . In addition ,
block locking means that query is now used and we don ' t need to
remove it .
2001-12-02 13:34:01 +01:00
*/
my_bool Query_cache_query : : try_lock_writing ( )
{
DBUG_ENTER ( " Query_cache_block::try_lock_writing " ) ;
2002-01-02 20:29:41 +01:00
if ( sem_trywait ( & lock ) ! = 0 | | clients ! = 0 )
2001-12-09 23:08:24 +01:00
{
2002-01-02 20:29:41 +01:00
DBUG_PRINT ( " info " , ( " can't lock semaphore " ) ) ;
2001-12-09 23:08:24 +01:00
DBUG_RETURN ( 0 ) ;
}
2002-01-02 20:29:41 +01:00
DBUG_PRINT ( " info " , ( " mutex 'lock' 0x%lx locked " , ( ulong ) & lock ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( 1 ) ;
}
void Query_cache_query : : lock_reading ( )
{
MUTEX_LOCK ( & clients_guard ) ;
2002-01-02 20:29:41 +01:00
if ( + + clients = = 1 )
SEM_LOCK ( & lock ) ;
2001-12-02 13:34:01 +01:00
MUTEX_UNLOCK ( & clients_guard ) ;
}
void Query_cache_query : : unlock_writing ( )
{
2002-01-02 20:29:41 +01:00
SEM_UNLOCK ( & lock ) ;
2001-12-02 13:34:01 +01:00
}
void Query_cache_query : : unlock_reading ( )
{
2002-01-02 20:29:41 +01:00
/*
To avoid unlocking semaphore before unlocking mutex ( that may cause
destroying locked mutex ) , we use temporary boolean variable ' unlock ' .
*/
2001-12-02 13:34:01 +01:00
MUTEX_LOCK ( & clients_guard ) ;
2002-01-02 20:29:41 +01:00
bool ulock = ( ( - - clients ) = = 0 ) ;
2001-12-02 13:34:01 +01:00
MUTEX_UNLOCK ( & clients_guard ) ;
2002-01-02 20:29:41 +01:00
if ( ulock ) SEM_UNLOCK ( & lock ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
extern " C "
{
byte * query_cache_query_get_key ( const byte * record , uint * length ,
my_bool not_used )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block = ( Query_cache_block * ) record ;
2001-12-02 13:34:01 +01:00
* length = ( query_block - > used - query_block - > headers_len ( ) -
ALIGN_SIZE ( sizeof ( Query_cache_query ) ) ) ;
2001-12-05 12:03:00 +01:00
return ( ( ( byte * ) query_block - > data ( ) ) +
2001-12-02 13:34:01 +01:00
ALIGN_SIZE ( sizeof ( Query_cache_query ) ) ) ;
}
}
/*****************************************************************************
2001-12-22 14:13:31 +01:00
Functions to store things into the query cache
2001-12-05 12:03:00 +01:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
2001-12-22 14:13:31 +01:00
/*
Insert the packet into the query cache .
This should only be called if net - > query_cache_query ! = 0
*/
2001-12-02 13:34:01 +01:00
void query_cache_insert ( NET * net , const char * packet , ulong length )
{
DBUG_ENTER ( " query_cache_insert " ) ;
# ifndef DBUG_OFF
2001-12-06 00:05:30 +01:00
// Check if we have called query_cache.wreck() (which disables the cache)
2001-12-02 13:34:01 +01:00
if ( query_cache . query_cache_size = = 0 )
DBUG_VOID_RETURN ;
# endif
2001-12-22 14:13:31 +01:00
STRUCT_LOCK ( & query_cache . structure_guard_mutex ) ;
Query_cache_block * query_block = ( ( Query_cache_block * )
net - > query_cache_query ) ;
if ( query_block )
2001-12-02 13:34:01 +01:00
{
2001-12-22 14:13:31 +01:00
Query_cache_query * header = query_block - > query ( ) ;
Query_cache_block * result = header - > result ( ) ;
2001-12-05 12:03:00 +01:00
2001-12-22 14:13:31 +01:00
DUMP ( & query_cache ) ;
BLOCK_LOCK_WR ( query_block ) ;
DBUG_PRINT ( " qcache " , ( " insert packet %lu bytes long " , length ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-22 14:13:31 +01:00
/*
On success STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) will be
done by query_cache . append_result_data if success ( if not we need
query_cache . structure_guard_mutex locked to free query )
*/
if ( ! query_cache . append_result_data ( & result , length , ( gptr ) packet ,
query_block ) )
{
query_cache . refused + + ;
DBUG_PRINT ( " warning " , ( " Can't append data " ) ) ;
2001-12-02 13:34:01 +01:00
header - > result ( result ) ;
2001-12-22 14:13:31 +01:00
DBUG_PRINT ( " qcache " , ( " free query 0x%lx " , ( ulong ) query_block ) ) ;
// The following call will remove the lock on query_block
query_cache . free_query ( query_block ) ;
// append_result_data no success => we need unlock
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) ;
2001-12-22 14:13:31 +01:00
DBUG_VOID_RETURN ;
}
header - > result ( result ) ;
BLOCK_UNLOCK_WR ( query_block ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-22 14:13:31 +01:00
else
STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) ;
2002-01-03 18:04:01 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 0 ) ; ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void query_cache_abort ( NET * net )
{
DBUG_ENTER ( " query_cache_abort " ) ;
# ifndef DBUG_OFF
2001-12-06 00:05:30 +01:00
// Check if we have called query_cache.wreck() (which disables the cache)
2001-12-02 13:34:01 +01:00
if ( query_cache . query_cache_size = = 0 )
DBUG_VOID_RETURN ;
# endif
2001-12-05 12:03:00 +01:00
if ( net - > query_cache_query ! = 0 ) // Quick check on unlocked structure
2001-12-02 13:34:01 +01:00
{
STRUCT_LOCK ( & query_cache . structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block = ( ( Query_cache_block * )
2001-12-02 13:34:01 +01:00
net - > query_cache_query ) ;
2001-12-05 12:03:00 +01:00
if ( query_block ) // Test if changed by other thread
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DUMP ( & query_cache ) ;
2001-12-02 13:34:01 +01:00
BLOCK_LOCK_WR ( query_block ) ;
2001-12-05 12:03:00 +01:00
// The following call will remove the lock on query_block
2001-12-02 13:34:01 +01:00
query_cache . free_query ( query_block ) ;
}
2001-12-22 14:13:31 +01:00
net - > query_cache_query = 0 ;
2002-01-05 21:51:42 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 1 ) ; ) ;
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
void query_cache_end_of_result ( NET * net )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " query_cache_end_of_result " ) ;
# ifndef DBUG_OFF
2001-12-06 00:05:30 +01:00
// Check if we have called query_cache.wreck() (which disables the cache)
2001-12-02 13:34:01 +01:00
if ( query_cache . query_cache_size = = 0 ) DBUG_VOID_RETURN ;
# endif
2001-12-05 12:03:00 +01:00
if ( net - > query_cache_query ! = 0 ) // Quick check on unlocked structure
2001-12-02 13:34:01 +01:00
{
STRUCT_LOCK ( & query_cache . structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block = ( ( Query_cache_block * )
net - > query_cache_query ) ;
if ( query_block )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DUMP ( & query_cache ) ;
2001-12-02 13:34:01 +01:00
BLOCK_LOCK_WR ( query_block ) ;
2002-01-12 14:40:52 +01:00
Query_cache_query * header = query_block - > query ( ) ;
Query_cache_block * last_result_block = header - > result ( ) - > prev ;
ulong allign_size = ALIGN_SIZE ( last_result_block - > used ) ;
ulong len = max ( query_cache . min_allocation_unit , allign_size ) ;
if ( last_result_block - > length > = query_cache . min_allocation_unit + len )
query_cache . split_block ( last_result_block , len ) ;
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) ;
# ifndef DBUG_OFF
2001-12-05 12:03:00 +01:00
if ( header - > result ( ) = = 0 )
2001-12-02 13:34:01 +01:00
{
DBUG_PRINT ( " error " , ( " end of data whith no result. query '%s' " ,
header - > query ( ) ) ) ;
query_cache . wreck ( __LINE__ , " " ) ;
DBUG_VOID_RETURN ;
}
# endif
2001-12-05 12:03:00 +01:00
header - > found_rows ( current_thd - > limit_found_rows ) ;
header - > result ( ) - > type = Query_cache_block : : RESULT ;
2001-12-02 13:34:01 +01:00
header - > writer ( 0 ) ;
BLOCK_UNLOCK_WR ( query_block ) ;
}
else
{
2001-12-05 12:03:00 +01:00
// Cache was flushed or resized and query was deleted => do nothing
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) ;
}
net - > query_cache_query = 0 ;
2002-01-03 18:04:01 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 0 ) ; ) ;
2001-12-02 13:34:01 +01:00
}
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
void query_cache_invalidate_by_MyISAM_filename ( const char * filename )
2001-12-02 13:34:01 +01:00
{
query_cache . invalidate_by_MyISAM_filename ( filename ) ;
2002-01-03 18:04:01 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 0 ) ; ) ;
2001-12-02 13:34:01 +01:00
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query_cache methods
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
Query_cache : : Query_cache ( ulong query_cache_limit ,
2001-12-02 13:34:01 +01:00
ulong min_allocation_unit ,
ulong min_result_data_size ,
uint def_query_hash_size ,
2001-12-05 12:03:00 +01:00
uint def_table_hash_size )
: query_cache_size ( 0 ) ,
query_cache_limit ( query_cache_limit ) ,
2001-12-06 00:05:30 +01:00
queries_in_cache ( 0 ) , hits ( 0 ) , inserts ( 0 ) , refused ( 0 ) ,
2001-12-09 23:08:24 +01:00
total_blocks ( 0 ) ,
2001-12-05 12:03:00 +01:00
min_allocation_unit ( min_allocation_unit ) ,
min_result_data_size ( min_result_data_size ) ,
def_query_hash_size ( def_query_hash_size ) ,
def_table_hash_size ( def_table_hash_size ) ,
initialized ( 0 )
{
ulong min_needed = ( ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_block_table ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_query ) ) + 3 ) ;
set_if_bigger ( min_allocation_unit , min_needed ) ;
2001-12-02 13:34:01 +01:00
this - > min_allocation_unit = min_allocation_unit ;
2001-12-05 12:03:00 +01:00
set_if_bigger ( this - > min_result_data_size , min_allocation_unit ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
ulong Query_cache : : resize ( ulong query_cache_size )
{
DBUG_ENTER ( " Query_cache::resize " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " from %lu to %lu " , this - > query_cache_size ,
query_cache_size ) ) ;
2001-12-02 13:34:01 +01:00
free_cache ( 0 ) ;
this - > query_cache_size = query_cache_size ;
DBUG_RETURN ( init_cache ( ) ) ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : store_query ( THD * thd , TABLE_LIST * tables_used )
{
2001-12-05 12:03:00 +01:00
TABLE_COUNTER_TYPE tables ;
2001-12-22 14:13:31 +01:00
ulong tot_length ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::store_query " ) ;
if ( query_cache_size = = 0 )
DBUG_VOID_RETURN ;
2001-12-05 12:03:00 +01:00
if ( ( tables = is_cacheable ( thd , thd - > query_length ,
thd - > query , & thd - > lex , tables_used ) ) )
{
NET * net = & thd - > net ;
byte flags = ( thd - > client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0 ) ;
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
if ( query_cache_size = = 0 )
DBUG_VOID_RETURN ;
DUMP ( this ) ;
2001-12-22 14:13:31 +01:00
/* Key is query + database + flag */
if ( thd - > db_length )
{
2001-12-23 01:43:46 +01:00
memcpy ( thd - > query + thd - > query_length + 1 , thd - > db , thd - > db_length ) ;
2001-12-22 14:13:31 +01:00
DBUG_PRINT ( " qcache " , ( " database : %s length %u " ,
thd - > db , thd - > db_length ) ) ;
}
else
{
DBUG_PRINT ( " qcache " , ( " No active database " ) ) ;
}
2001-12-02 13:34:01 +01:00
/*
2001-12-05 12:03:00 +01:00
Prepare flags :
most significant bit - CLIENT_LONG_FLAG ,
other - charset number ( 0 no charset convertion )
2001-12-02 13:34:01 +01:00
*/
if ( thd - > convert_set ! = 0 )
{
2001-12-05 12:03:00 +01:00
flags | = ( byte ) thd - > convert_set - > number ( ) ;
DBUG_ASSERT ( thd - > convert_set - > number ( ) < 128 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-23 01:43:46 +01:00
tot_length = thd - > query_length + thd - > db_length + 2 ;
2001-12-22 14:13:31 +01:00
thd - > query [ tot_length - 1 ] = ( char ) flags ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/* Check if another thread is processing the same query? */
2001-12-02 13:34:01 +01:00
Query_cache_block * competitor = ( Query_cache_block * )
2001-12-22 14:13:31 +01:00
hash_search ( & queries , ( byte * ) thd - > query , tot_length ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " competitor 0x%lx, flags %x " , ( ulong ) competitor ,
2001-12-02 13:34:01 +01:00
flags ) ) ;
if ( competitor = = 0 )
{
2001-12-05 12:03:00 +01:00
/* Query is not in cache and no one is working with it; Store it */
Query_cache_block * query_block ;
2001-12-22 14:13:31 +01:00
query_block = write_block_data ( tot_length , ( gptr ) thd - > query ,
2001-12-05 12:03:00 +01:00
ALIGN_SIZE ( sizeof ( Query_cache_query ) ) ,
Query_cache_block : : QUERY , tables , 1 ) ;
2001-12-02 13:34:01 +01:00
if ( query_block ! = 0 )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " query block 0x%lx allocated, %lu " ,
( ulong ) query_block , query_block - > used ) ) ;
2000-07-31 21:29:14 +02:00
2001-12-05 12:03:00 +01:00
Query_cache_query * header = query_block - > query ( ) ;
2001-12-02 13:34:01 +01:00
header - > init_n_lock ( ) ;
2001-12-05 12:03:00 +01:00
if ( hash_insert ( & queries , ( byte * ) query_block ) )
2001-12-02 13:34:01 +01:00
{
refused + + ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " insertion in query hash " ) ) ;
2001-12-02 13:34:01 +01:00
header - > unlock_n_destroy ( ) ;
free_memory_block ( query_block ) ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
goto end ;
2001-12-02 13:34:01 +01:00
}
if ( ! register_all_tables ( query_block , tables_used , tables ) )
{
refused + + ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " warning " , ( " tables list including failed " ) ) ;
2001-12-09 23:08:24 +01:00
hash_delete ( & queries , ( byte * ) query_block ) ;
2001-12-02 13:34:01 +01:00
header - > unlock_n_destroy ( ) ;
free_memory_block ( query_block ) ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-22 14:13:31 +01:00
goto end ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
double_linked_list_simple_include ( query_block , & queries_blocks ) ;
2001-12-02 13:34:01 +01:00
inserts + + ;
queries_in_cache + + ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2000-07-31 21:29:14 +02:00
2001-12-09 23:08:24 +01:00
net - > query_cache_query = ( gptr ) query_block ;
2001-12-02 13:34:01 +01:00
header - > writer ( net ) ;
// init_n_lock make query block locked
BLOCK_UNLOCK_WR ( query_block ) ;
}
else
{
// We have not enough memory to store query => do nothing
2001-12-05 12:03:00 +01:00
refused + + ;
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
DBUG_PRINT ( " warning " , ( " Can't allocate query " ) ) ;
}
}
else
{
2001-12-05 12:03:00 +01:00
// Another thread is processing the same query => do nothing
2001-12-02 13:34:01 +01:00
refused + + ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Another thread process same query " ) ) ;
2001-12-02 13:34:01 +01:00
}
}
else
2001-12-06 00:05:30 +01:00
statistic_increment ( refused , & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
end :
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2000-07-31 21:29:14 +02:00
2001-12-22 14:13:31 +01:00
/*
Check if the query is in the cache . If it was cached , send it
to the user .
RESULTS
1 Query was not cached .
0 The query was cached and user was sent the result .
- 1 The query was cached but we didn ' t have rights to use it .
No error is sent to the client yet .
*/
2001-12-02 13:34:01 +01:00
2001-12-22 14:13:31 +01:00
int
2001-12-05 12:03:00 +01:00
Query_cache : : send_result_to_client ( THD * thd , char * sql , uint query_length )
{
Query_cache_query * query ;
Query_cache_block * first_result_block , * result_block ;
Query_cache_block_table * block_table , * block_table_end ;
2001-12-22 14:13:31 +01:00
ulong tot_length ;
2001-12-05 12:03:00 +01:00
byte flags ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::send_result_to_client " ) ;
if ( query_cache_size = = 0 | |
/*
it is not possible to check has_transactions ( ) function of handler
because tables not opened yet
*/
( thd - > options & ( OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN ) ) | |
thd - > query_cache_type = = 0 )
{
2001-12-22 14:13:31 +01:00
DBUG_PRINT ( " qcache " , ( " query cache disabled or not in autocommit mode " ) ) ;
2001-12-05 12:03:00 +01:00
goto err ;
}
2001-12-22 14:13:31 +01:00
/* Check that we haven't forgot to reset the query cache variables */
DBUG_ASSERT ( thd - > net . query_cache_query = = 0 ) ;
2002-01-08 07:48:05 +01:00
if ( ! thd - > safe_to_cache_query )
2001-12-05 12:03:00 +01:00
{
2002-01-08 07:38:33 +01:00
DBUG_PRINT ( " qcache " , ( " SELECT is non-cacheable " ) ) ;
2001-12-05 12:03:00 +01:00
goto err ;
2001-12-02 13:34:01 +01:00
}
2001-12-23 01:43:46 +01:00
/*
Test if the query is a SELECT
( pre - space is removed in dispatch_command )
*/
2002-03-12 18:37:58 +01:00
if ( my_toupper ( system_charset_info , sql [ 0 ] ) ! = ' S ' | |
my_toupper ( system_charset_info , sql [ 1 ] ) ! = ' E ' | |
my_toupper ( system_charset_info , sql [ 2 ] ) ! = ' L ' )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " The statement is not a SELECT; Not cached " ) ) ;
goto err ;
2001-12-02 13:34:01 +01:00
}
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( query_cache_size = = 0 )
{
2001-12-22 14:13:31 +01:00
DBUG_PRINT ( " qcache " , ( " query cache disabled " ) ) ;
goto err_unlock ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block ;
2001-12-02 13:34:01 +01:00
2001-12-23 01:43:46 +01:00
tot_length = query_length + thd - > db_length + 2 ;
2001-12-22 14:13:31 +01:00
if ( thd - > db_length )
{
2001-12-23 01:43:46 +01:00
memcpy ( sql + query_length + 1 , thd - > db , thd - > db_length ) ;
2001-12-22 14:13:31 +01:00
DBUG_PRINT ( " qcache " , ( " database: '%s' length %u " ,
thd - > db , thd - > db_length ) ) ;
}
else
{
DBUG_PRINT ( " qcache " , ( " No active database " ) ) ;
}
2001-12-02 13:34:01 +01:00
/*
prepare flags :
2001-12-05 12:03:00 +01:00
Most significant bit - CLIENT_LONG_FLAG ,
Other - charset number ( 0 no charset convertion )
2001-12-02 13:34:01 +01:00
*/
2001-12-05 12:03:00 +01:00
flags = ( thd - > client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0 ) ;
2001-12-02 13:34:01 +01:00
if ( thd - > convert_set ! = 0 )
{
flags | = ( byte ) thd - > convert_set - > number ( ) ;
2001-12-05 12:03:00 +01:00
DBUG_ASSERT ( thd - > convert_set - > number ( ) < 128 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-22 14:13:31 +01:00
sql [ tot_length - 1 ] = ( char ) flags ;
2001-12-09 23:08:24 +01:00
query_block = ( Query_cache_block * ) hash_search ( & queries , ( byte * ) sql ,
2001-12-22 14:13:31 +01:00
tot_length ) ;
2001-12-05 12:03:00 +01:00
/* Quick abort on unlocked data */
2001-12-02 13:34:01 +01:00
if ( query_block = = 0 | |
query_block - > query ( ) - > result ( ) = = 0 | |
query_block - > query ( ) - > result ( ) - > type ! = Query_cache_block : : RESULT )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " No query in query hash or no results " ) ) ;
2001-12-22 14:13:31 +01:00
goto err_unlock ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Query in query hash 0x%lx " , ( ulong ) query_block ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/* Now lock and test that nothing changed while blocks was unlocked */
2001-12-02 13:34:01 +01:00
BLOCK_LOCK_RD ( query_block ) ;
2001-12-05 12:03:00 +01:00
query = query_block - > query ( ) ;
result_block = first_result_block = query - > result ( ) ;
if ( result_block = = 0 | | result_block - > type ! = Query_cache_block : : RESULT )
{
/* The query is probably yet processed */
DBUG_PRINT ( " qcache " , ( " query found, but no data or data incomplete " ) ) ;
2001-12-06 00:05:30 +01:00
BLOCK_UNLOCK_RD ( query_block ) ;
2001-12-22 14:13:31 +01:00
goto err_unlock ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Query have result 0x%lx " , ( ulong ) query ) ) ;
// Check access;
block_table = query_block - > table ( 0 ) ;
block_table_end = block_table + query_block - > n_tables ;
for ( ; block_table ! = block_table_end ; block_table + + )
{
TABLE_LIST table_list ;
bzero ( ( char * ) & table_list , sizeof ( table_list ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
Query_cache_table * table = block_table - > parent ;
table_list . db = table - > db ( ) ;
table_list . name = table_list . real_name = table - > table ( ) ;
2001-12-22 14:13:31 +01:00
if ( check_table_access ( thd , SELECT_ACL , & table_list , 1 ) )
2001-12-05 12:03:00 +01:00
{
DBUG_PRINT ( " qcache " ,
( " probably no SELECT access to %s.%s => return to normal processing " ,
table_list . db , table_list . name ) ) ;
2001-12-22 14:13:31 +01:00
refused + + ; // This is actually a hit
2001-12-06 00:05:30 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-22 14:13:31 +01:00
thd - > safe_to_cache_query = 0 ; // Don't try to cache this
BLOCK_UNLOCK_RD ( query_block ) ;
DBUG_RETURN ( - 1 ) ; // Privilege error
}
if ( table_list . grant . want_privilege )
{
DBUG_PRINT ( " qcache " , ( " Need to check column privileges for %s.%s " ,
table_list . db , table_list . name ) ) ;
BLOCK_UNLOCK_RD ( query_block ) ;
thd - > safe_to_cache_query = 0 ; // Don't try to cache this
goto err_unlock ; // Parse query
2001-12-05 12:03:00 +01:00
}
}
2001-12-02 13:34:01 +01:00
move_to_query_list_end ( query_block ) ;
hits + + ;
2001-12-06 00:05:30 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
/*
Send cached result to client
*/
2001-12-02 13:34:01 +01:00
do
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Results (len %lu, used %lu, headers %lu) " ,
2001-12-02 13:34:01 +01:00
result_block - > length , result_block - > used ,
result_block - > headers_len ( ) +
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) ) ;
2001-12-05 12:03:00 +01:00
Query_cache_result * result = result_block - > result ( ) ;
if ( net_real_write ( & thd - > net , result - > data ( ) ,
result_block - > used -
result_block - > headers_len ( ) -
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) )
break ; // Client aborted
2001-12-02 13:34:01 +01:00
result_block = result_block - > next ;
} while ( result_block ! = first_result_block ) ;
thd - > limit_found_rows = query - > found_rows ( ) ;
BLOCK_UNLOCK_RD ( query_block ) ;
2001-12-22 14:13:31 +01:00
DBUG_RETURN ( 1 ) ; // Result sent to client
2001-12-05 12:03:00 +01:00
2001-12-22 14:13:31 +01:00
err_unlock :
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
err :
2001-12-22 14:13:31 +01:00
DBUG_RETURN ( 0 ) ; // Query was not cached
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
/*
Remove all cached queries that uses any of the tables in the list
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : invalidate ( TABLE_LIST * tables_used )
{
DBUG_ENTER ( " Query_cache::invalidate (table list) " ) ;
if ( query_cache_size > 0 )
{
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( query_cache_size > 0 )
{
DUMP ( this ) ;
for ( ; tables_used ; tables_used = tables_used - > next )
invalidate_table ( tables_used ) ;
}
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
/*
Remove all cached queries that uses the given table
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : invalidate ( TABLE * table )
{
DBUG_ENTER ( " Query_cache::invalidate (table) " ) ;
if ( query_cache_size > 0 )
{
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( query_cache_size > 0 )
invalidate_table ( table ) ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
2000-07-31 21:29:14 +02:00
}
2001-12-05 12:03:00 +01:00
/*
Remove all cached queries that uses the given table type .
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : invalidate ( Query_cache_table : : query_cache_table_type type )
2000-07-31 21:29:14 +02:00
{
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::invalidate (type) " ) ;
if ( query_cache_size > 0 )
{
STRUCT_LOCK ( & structure_guard_mutex ) ;
DUMP ( this ) ;
2001-12-09 23:08:24 +01:00
if ( query_cache_size > 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-09 23:08:24 +01:00
/* invalidate_table reduce list while only root of list remain */
while ( tables_blocks [ type ] ! = 0 )
invalidate_table ( tables_blocks [ type ] ) ;
2001-12-02 13:34:01 +01:00
}
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
2000-07-31 21:29:14 +02:00
}
2001-12-05 12:03:00 +01:00
/*
Remove all cached queries that uses the given database
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : invalidate ( char * db )
2000-07-31 21:29:14 +02:00
{
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::invalidate (db) " ) ;
if ( query_cache_size > 0 )
2000-07-31 21:29:14 +02:00
{
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( query_cache_size > 0 )
{
DUMP ( this ) ;
2001-12-09 23:08:24 +01:00
for ( int i = 0 ; i < ( int ) Query_cache_table : : TYPES_NUMBER ; i + + )
2001-12-02 13:34:01 +01:00
{
2001-12-09 23:08:24 +01:00
/* invalidate_table reduce list while only root of list remain */
while ( tables_blocks [ i ] ! = 0 )
invalidate_table ( tables_blocks [ i ] ) ;
2001-12-02 13:34:01 +01:00
}
}
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2000-07-31 21:29:14 +02:00
}
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
2000-07-31 21:29:14 +02:00
}
2001-12-05 12:03:00 +01:00
void Query_cache : : invalidate_by_MyISAM_filename ( const char * filename )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::invalidate_by_MyISAM_filename " ) ;
if ( query_cache_size > 0 )
{
2001-12-05 12:03:00 +01:00
/* Calculate the key outside the lock to make the lock shorter */
char key [ MAX_DBKEY_LENGTH ] ;
uint key_length = filename_2_table_key ( key , filename ) ;
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
if ( query_cache_size > 0 ) // Safety if cache removed
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block * table_block ;
2001-12-09 23:08:24 +01:00
if ( ( table_block = ( Query_cache_block * ) hash_search ( & tables ,
( byte * ) key ,
2001-12-05 12:03:00 +01:00
key_length ) ) )
invalidate_table ( table_block ) ;
2001-12-02 13:34:01 +01:00
}
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-06 00:05:30 +01:00
/* Remove all queries from cache */
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : flush ( )
2000-07-31 21:29:14 +02:00
{
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::flush " ) ;
2001-12-06 00:05:30 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
2001-12-02 13:34:01 +01:00
if ( query_cache_size > 0 )
{
DUMP ( this ) ;
flush_cache ( ) ;
DUMP ( this ) ;
}
2002-01-05 21:51:42 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 1 ) ; ) ;
2001-12-06 00:05:30 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
2000-07-31 21:29:14 +02:00
}
2001-12-06 00:05:30 +01:00
/* Join result in cache in 1 block (if result length > join_limit) */
2001-12-02 13:34:01 +01:00
void Query_cache : : pack ( ulong join_limit , uint iteration_limit )
{
DBUG_ENTER ( " Query_cache::pack " ) ;
uint i = 0 ;
do
{
pack_cache ( ) ;
} while ( ( + + i < iteration_limit ) & & join_results ( join_limit ) ) ;
DBUG_VOID_RETURN ;
}
2000-07-31 21:29:14 +02:00
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : destroy ( )
2000-07-31 21:29:14 +02:00
{
2002-01-09 08:38:48 +01:00
if ( ! initialized )
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
return ;
}
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::destroy " ) ;
free_cache ( 1 ) ;
pthread_mutex_destroy ( & structure_guard_mutex ) ;
initialized = 0 ;
DBUG_VOID_RETURN ;
}
2000-07-31 21:29:14 +02:00
2001-12-02 13:34:01 +01:00
/*****************************************************************************
2001-12-05 12:03:00 +01:00
init / destroy
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
void Query_cache : : init ( )
{
DBUG_ENTER ( " Query_cache::init " ) ;
pthread_mutex_init ( & structure_guard_mutex , MY_MUTEX_INIT_FAST ) ;
initialized = 1 ;
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
ulong Query_cache : : init_cache ( )
{
2001-12-05 12:03:00 +01:00
uint mem_bin_count , num , step ;
ulong mem_bin_size , prev_size , inc ;
ulong additional_data_size , max_mem_bin_size , approx_additional_data_size ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::init_cache " ) ;
if ( ! initialized )
init ( ) ;
2001-12-05 12:03:00 +01:00
approx_additional_data_size = ( sizeof ( Query_cache ) +
sizeof ( gptr ) * ( def_query_hash_size +
def_query_hash_size ) ) ;
2001-12-02 13:34:01 +01:00
if ( query_cache_size < approx_additional_data_size )
2001-12-05 12:03:00 +01:00
goto err ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
query_cache_size - = approx_additional_data_size ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/*
Count memory bins number .
Check section 6. in start comment for the used algorithm .
*/
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
max_mem_bin_size = query_cache_size > > QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 ;
mem_bin_count = ( uint ) ( ( 1 + QUERY_CACHE_MEM_BIN_PARTS_INC ) *
QUERY_CACHE_MEM_BIN_PARTS_MUL ) ;
mem_bin_num = 1 ;
mem_bin_steps = 1 ;
mem_bin_size = max_mem_bin_size > > QUERY_CACHE_MEM_BIN_STEP_PWR2 ;
prev_size = 0 ;
while ( mem_bin_size > min_allocation_unit )
{
mem_bin_num + = mem_bin_count ;
prev_size = mem_bin_size ;
mem_bin_size > > = QUERY_CACHE_MEM_BIN_STEP_PWR2 ;
2001-12-02 13:34:01 +01:00
mem_bin_steps + + ;
2001-12-05 12:03:00 +01:00
mem_bin_count + = QUERY_CACHE_MEM_BIN_PARTS_INC ;
mem_bin_count = ( uint ) ( mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL ) ;
// Prevent too small bins spacing
if ( mem_bin_count > ( mem_bin_size > > QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 ) )
mem_bin_count = ( mem_bin_size > > QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
inc = ( prev_size - mem_bin_size ) / mem_bin_count ;
mem_bin_num + = ( mem_bin_count - ( min_allocation_unit - mem_bin_size ) / inc ) ;
mem_bin_steps + + ;
additional_data_size = ( ( mem_bin_num + 1 ) *
ALIGN_SIZE ( sizeof ( Query_cache_memory_bin ) ) +
( mem_bin_steps *
ALIGN_SIZE ( sizeof ( Query_cache_memory_bin_step ) ) ) ) ;
2001-12-02 13:34:01 +01:00
if ( query_cache_size < additional_data_size )
2001-12-05 12:03:00 +01:00
goto err ;
query_cache_size - = additional_data_size ;
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( query_cache_size < = min_allocation_unit )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " ,
2001-12-02 13:34:01 +01:00
( " query_cache_size <= min_allocation_unit => cache disabled " ) ) ;
2001-12-05 12:03:00 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
goto err ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
if ( ! ( cache = ( byte * )
my_malloc_lock ( query_cache_size + additional_data_size , MYF ( 0 ) ) ) )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
goto err ;
}
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " cache length %lu, min unit %lu, %u bins " ,
query_cache_size , min_allocation_unit , mem_bin_num ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
steps = ( Query_cache_memory_bin_step * ) cache ;
bins = ( ( Query_cache_memory_bin * )
( cache + mem_bin_steps *
ALIGN_SIZE ( sizeof ( Query_cache_memory_bin_step ) ) ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
first_block = ( Query_cache_block * ) ( cache + additional_data_size ) ;
first_block - > init ( query_cache_size ) ;
2001-12-09 23:08:24 +01:00
total_blocks + + ;
2001-12-05 12:03:00 +01:00
first_block - > pnext = first_block - > pprev = first_block ;
first_block - > next = first_block - > prev = first_block ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/* Prepare bins */
bins [ 0 ] . init ( max_mem_bin_size ) ;
steps [ 0 ] . init ( max_mem_bin_size , 0 , 0 ) ;
mem_bin_count = ( uint ) ( ( 1 + QUERY_CACHE_MEM_BIN_PARTS_INC ) *
QUERY_CACHE_MEM_BIN_PARTS_MUL ) ;
num = step = 1 ;
mem_bin_size = max_mem_bin_size > > QUERY_CACHE_MEM_BIN_STEP_PWR2 ;
while ( mem_bin_size > min_allocation_unit )
{
ulong incr = ( steps [ step - 1 ] . size - mem_bin_size ) / mem_bin_count ;
unsigned long size = mem_bin_size ;
for ( uint i = mem_bin_count ; i > 0 ; i - - )
{
bins [ num + i - 1 ] . init ( size ) ;
size + = incr ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
num + = mem_bin_count ;
steps [ step ] . init ( mem_bin_size , num - 1 , incr ) ;
mem_bin_size > > = QUERY_CACHE_MEM_BIN_STEP_PWR2 ;
step + + ;
mem_bin_count + = QUERY_CACHE_MEM_BIN_PARTS_INC ;
mem_bin_count = ( uint ) ( mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL ) ;
if ( mem_bin_count > ( mem_bin_size > > QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 ) )
mem_bin_count = ( mem_bin_size > > QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
inc = ( steps [ step - 1 ] . size - mem_bin_size ) / mem_bin_count ;
/*
num + mem_bin_count > mem_bin_num , but index never be > mem_bin_num
because block with size < min_allocated_unit never will be requested
*/
steps [ step ] . init ( mem_bin_size , num + mem_bin_count - 1 , inc ) ;
{
uint skiped = ( min_allocation_unit - mem_bin_size ) / inc ;
ulong size = mem_bin_size + inc * skiped ;
uint i = mem_bin_count - skiped ;
while ( i - - > 0 )
{
bins [ num + i ] . init ( size ) ;
size + = inc ;
}
}
2001-12-09 23:08:24 +01:00
bins [ mem_bin_num ] . number = 1 ; // For easy end test in get_free_block
free_memory = free_memory_blocks = 0 ;
2001-12-05 12:03:00 +01:00
insert_into_free_memory_list ( first_block ) ;
DUMP ( this ) ;
VOID ( hash_init ( & queries , def_query_hash_size , 0 , 0 ,
query_cache_query_get_key , 0 , 0 ) ) ;
VOID ( hash_init ( & tables , def_table_hash_size , 0 , 0 ,
query_cache_table_get_key , 0 , 0 ) ) ;
2001-12-02 13:34:01 +01:00
queries_in_cache = 0 ;
queries_blocks = 0 ;
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
DBUG_RETURN ( query_cache_size +
additional_data_size + approx_additional_data_size ) ;
2001-12-05 12:03:00 +01:00
err :
make_disabled ( ) ;
DBUG_RETURN ( 0 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
/* Disable the use of the query cache */
2001-12-02 13:34:01 +01:00
void Query_cache : : make_disabled ( )
{
DBUG_ENTER ( " Query_cache::make_disabled " ) ;
2001-12-05 12:03:00 +01:00
query_cache_size = 0 ;
free_memory = 0 ;
bins = 0 ;
steps = 0 ;
cache = 0 ;
mem_bin_num = mem_bin_steps = 0 ;
queries_in_cache = 0 ;
first_block = 0 ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : free_cache ( my_bool destruction )
{
DBUG_ENTER ( " Query_cache::free_cache " ) ;
if ( query_cache_size > 0 )
{
if ( ! destruction )
STRUCT_LOCK ( & structure_guard_mutex ) ;
flush_cache ( ) ;
# ifndef DBUG_OFF
if ( bins [ 0 ] . free_blocks = = 0 )
{
wreck ( __LINE__ , " no free memory found in (bins[0].free_blocks " ) ;
2001-12-13 01:31:19 +01:00
DBUG_VOID_RETURN ;
2001-12-02 13:34:01 +01:00
}
# endif
2001-12-05 12:03:00 +01:00
/* Becasue we did a flush, all cache memory must be in one this block */
bins [ 0 ] . free_blocks - > destroy ( ) ;
2001-12-09 23:08:24 +01:00
total_blocks - - ;
2002-01-02 20:29:41 +01:00
# ifndef DBUG_OFF
if ( free_memory ! = query_cache_size )
DBUG_PRINT ( " qcache " , ( " free memory %lu (should be %lu) " ,
free_memory , query_cache_size ) ) ;
# endif
2001-12-05 12:03:00 +01:00
my_free ( ( gptr ) cache , MYF ( MY_ALLOW_ZERO_PTR ) ) ;
make_disabled ( ) ;
2001-12-02 13:34:01 +01:00
hash_free ( & queries ) ;
hash_free ( & tables ) ;
if ( ! destruction )
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
}
DBUG_VOID_RETURN ;
}
/*****************************************************************************
2001-12-13 01:31:19 +01:00
Free block data
2001-12-05 12:03:00 +01:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
The following assumes we have a lock on the cache
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : flush_cache ( )
{
2001-12-05 12:03:00 +01:00
while ( queries_blocks ! = 0 )
{
2001-12-02 13:34:01 +01:00
BLOCK_LOCK_WR ( queries_blocks ) ;
free_query ( queries_blocks ) ;
}
}
2001-12-05 12:03:00 +01:00
/*
Free oldest query that is not in use by another thread .
Returns 1 if we couldn ' t remove anything
*/
2001-12-02 13:34:01 +01:00
my_bool Query_cache : : free_old_query ( )
{
DBUG_ENTER ( " Query_cache::free_old_query " ) ;
2001-12-06 00:05:30 +01:00
if ( queries_blocks )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
/*
try_lock_writing used to prevent client because here lock
sequence is breached .
Also we don ' t need remove locked queries at this point .
*/
Query_cache_block * query_block = 0 ;
if ( queries_blocks ! = 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block * block = queries_blocks ;
/* Search until we find first query that we can remove */
do
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_query * header = block - > query ( ) ;
if ( header - > result ( ) ! = 0 & &
header - > result ( ) - > type = = Query_cache_block : : RESULT & &
block - > query ( ) - > try_lock_writing ( ) )
{
query_block = block ;
break ;
}
} while ( ( block = block - > next ) ! = queries_blocks ) ;
}
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
if ( query_block ! = 0 )
{
free_query ( query_block ) ;
DBUG_RETURN ( 0 ) ;
}
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
DBUG_RETURN ( 1 ) ; // Nothing to remove
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
/*
Free query from query cache .
query_block must be locked for writing .
This function will remove ( and destroy ) the lock for the query .
*/
void Query_cache : : free_query ( Query_cache_block * query_block )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::free_query " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " free query 0x%lx %lu bytes result " ,
( ulong ) query_block ,
2001-12-02 13:34:01 +01:00
query_block - > query ( ) - > length ( ) ) ) ;
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
queries_in_cache - - ;
hash_delete ( & queries , ( byte * ) query_block ) ;
2001-12-05 12:03:00 +01:00
Query_cache_query * query = query_block - > query ( ) ;
2001-12-02 13:34:01 +01:00
if ( query - > writer ( ) ! = 0 )
{
2001-12-05 12:03:00 +01:00
/* Tell MySQL that this query should not be cached anymore */
2001-12-02 13:34:01 +01:00
query - > writer ( ) - > query_cache_query = 0 ;
query - > writer ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
double_linked_list_exclude ( query_block , & queries_blocks ) ;
Query_cache_block_table * table = query_block - > table ( 0 ) ;
for ( TABLE_COUNTER_TYPE i = 0 ; i < query_block - > n_tables ; i + + )
unlink_table ( table + + ) ;
2001-12-02 13:34:01 +01:00
Query_cache_block * result_block = query - > result ( ) ;
2001-12-06 00:05:30 +01:00
/*
The following is true when query destruction was called and no results
in query . ( query just registered and then abort / pack / flush called )
*/
2001-12-02 13:34:01 +01:00
if ( result_block ! = 0 )
{
2001-12-05 12:03:00 +01:00
Query_cache_block * block = result_block ;
2001-12-02 13:34:01 +01:00
do
{
2001-12-05 12:03:00 +01:00
Query_cache_block * current = block ;
2001-12-02 13:34:01 +01:00
block = block - > next ;
free_memory_block ( current ) ;
} while ( block ! = result_block ) ;
}
query - > unlock_n_destroy ( ) ;
free_memory_block ( query_block ) ;
DBUG_VOID_RETURN ;
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query data creation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
Query_cache_block *
Query_cache : : write_block_data ( ulong data_len , gptr data ,
ulong header_len ,
Query_cache_block : : block_type type ,
TABLE_COUNTER_TYPE ntab ,
my_bool under_guard )
{
2001-12-05 12:03:00 +01:00
ulong all_headers_len = ( ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( ntab * sizeof ( Query_cache_block_table ) ) +
header_len ) ;
ulong len = data_len + all_headers_len ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::write_block_data " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " data: %ld, header: %ld, all header: %ld " ,
2001-12-02 13:34:01 +01:00
data_len , header_len , all_headers_len ) ) ;
2001-12-05 12:03:00 +01:00
Query_cache_block * block = allocate_block ( max ( len , min_allocation_unit ) ,
1 , 0 , under_guard ) ;
2001-12-02 13:34:01 +01:00
if ( block ! = 0 )
{
block - > type = type ;
block - > n_tables = ntab ;
block - > used = len ;
2001-12-05 12:03:00 +01:00
memcpy ( ( void * ) ( ( ( byte * ) block ) + all_headers_len ) ,
( void * ) data , data_len ) ;
2001-12-02 13:34:01 +01:00
}
DBUG_RETURN ( block ) ;
}
2001-12-05 12:03:00 +01:00
/*
On success STRUCT_UNLOCK ( & query_cache . structure_guard_mutex ) will be done .
*/
2001-12-02 13:34:01 +01:00
my_bool
2001-12-05 12:03:00 +01:00
Query_cache : : append_result_data ( Query_cache_block * * current_block ,
2001-12-02 13:34:01 +01:00
ulong data_len , gptr data ,
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::append_result_data " ) ;
DBUG_PRINT ( " qcache " , ( " append %lu bytes to 0x%lx query " ,
data_len , query_block ) ) ;
2001-12-02 13:34:01 +01:00
if ( query_block - > query ( ) - > add ( data_len ) > query_cache_limit )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " size limit reached %lu > %lu " ,
2001-12-02 13:34:01 +01:00
query_block - > query ( ) - > length ( ) ,
query_cache_limit ) ) ;
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
if ( * current_block = = 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " allocated first result data block %lu " , data_len ) ) ;
2001-12-02 13:34:01 +01:00
/*
2001-12-05 12:03:00 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) Will be done by
write_result_data if success ;
2001-12-02 13:34:01 +01:00
*/
2001-12-05 12:03:00 +01:00
DBUG_RETURN ( write_result_data ( current_block , data_len , data , query_block ,
2001-12-02 13:34:01 +01:00
Query_cache_block : : RES_BEG ) ) ;
}
2001-12-05 12:03:00 +01:00
Query_cache_block * last_block = ( * current_block ) - > prev ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " lastblock 0x%lx len %lu used %lu " ,
( ulong ) last_block , last_block - > length ,
2001-12-02 13:34:01 +01:00
last_block - > used ) ) ;
my_bool success = 1 ;
2001-12-05 12:03:00 +01:00
ulong last_block_free_space = last_block - > length - last_block - > used ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/*
We will first allocate and write the ' tail ' of data , that doesn ' t fit
in the ' last_block ' . Only if this succeeds , we will fill the last_block .
This saves us a memcpy if the query doesn ' t fit in the query cache .
*/
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
// Try join blocks if physically next block is free...
2002-01-12 14:40:52 +01:00
ulong tail = data_len - last_block_free_space ;
ulong append_min = get_min_append_result_data_size ( ) ;
2001-12-02 13:34:01 +01:00
if ( last_block_free_space < data_len & &
append_next_free_block ( last_block ,
2002-01-12 14:40:52 +01:00
max ( tail , append_min ) ) )
2001-12-02 13:34:01 +01:00
last_block_free_space = last_block - > length - last_block - > used ;
2001-12-05 12:03:00 +01:00
// If no space in last block (even after join) allocate new block
2001-12-02 13:34:01 +01:00
if ( last_block_free_space < data_len )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " allocate new block for %lu bytes " ,
2001-12-02 13:34:01 +01:00
data_len - last_block_free_space ) ) ;
Query_cache_block * new_block = 0 ;
/*
2001-12-05 12:03:00 +01:00
On success STRUCT_UNLOCK ( & structure_guard_mutex ) will be done
by the next call
2001-12-02 13:34:01 +01:00
*/
2001-12-05 12:03:00 +01:00
success = write_result_data ( & new_block , data_len - last_block_free_space ,
2001-12-02 13:34:01 +01:00
( gptr ) ( ( ( byte * ) data ) + last_block_free_space ) ,
query_block ,
Query_cache_block : : RES_CONT ) ;
/*
2001-12-13 01:31:19 +01:00
new_block may be ! = 0 even ! success ( if write_result_data
allocate a small block but failed to allocate continue )
2001-12-02 13:34:01 +01:00
*/
if ( new_block ! = 0 )
double_linked_list_join ( last_block , new_block ) ;
}
else
2001-12-05 12:03:00 +01:00
{
// It is success (nobody can prevent us write data)
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
}
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
// Now finally write data to the last block
if ( success & & last_block_free_space > 0 )
2001-12-02 13:34:01 +01:00
{
ulong to_copy = min ( data_len , last_block_free_space ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " use free space %lub at block 0x%lx to copy %lub " ,
2001-12-02 13:34:01 +01:00
last_block_free_space , ( ulong ) last_block , to_copy ) ) ;
2001-12-05 12:03:00 +01:00
memcpy ( ( void * ) ( ( ( byte * ) last_block ) + last_block - > used ) , ( void * ) data ,
to_copy ) ;
2001-12-02 13:34:01 +01:00
last_block - > used + = to_copy ;
}
DBUG_RETURN ( success ) ;
}
2001-12-05 12:03:00 +01:00
my_bool Query_cache : : write_result_data ( Query_cache_block * * result_block ,
2001-12-02 13:34:01 +01:00
ulong data_len , gptr data ,
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block ,
2001-12-02 13:34:01 +01:00
Query_cache_block : : block_type type )
{
DBUG_ENTER ( " Query_cache::write_result_data " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " data_len %lu " , data_len ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-06 00:05:30 +01:00
/*
Reserve block ( s ) for filling
During data allocation we must have structure_guard_mutex locked .
As data copy is not a fast operation , it ' s better if we don ' t have
structure_guard_mutex locked during data coping .
Thus we first allocate space and lock query , then unlock
structure_guard_mutex and copy data .
*/
2002-01-12 14:40:52 +01:00
my_bool success = allocate_data_chain ( result_block , data_len , query_block ,
type = = Query_cache_block : : RES_BEG ) ;
2001-12-02 13:34:01 +01:00
if ( success )
{
2001-12-05 12:03:00 +01:00
// It is success (nobody can prevent us write data)
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-05 12:03:00 +01:00
byte * rest = ( byte * ) data ;
Query_cache_block * block = * result_block ;
uint headers_len = ( ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) ;
// Now fill list of blocks that created by allocate_data_chain
2001-12-02 13:34:01 +01:00
do
{
block - > type = type ;
ulong length = block - > used - headers_len ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " write %lu byte in block 0x%lx " , length ,
2001-12-02 13:34:01 +01:00
( ulong ) block ) ) ;
2001-12-05 12:03:00 +01:00
memcpy ( ( void * ) ( ( ( byte * ) block ) + headers_len ) , ( void * ) rest , length ) ;
2001-12-02 13:34:01 +01:00
rest + = length ;
block = block - > next ;
type = Query_cache_block : : RES_CONT ;
2001-12-05 12:03:00 +01:00
} while ( block ! = * result_block ) ;
2001-12-02 13:34:01 +01:00
}
else
{
2001-12-05 12:03:00 +01:00
if ( * result_block ! = 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
// Destroy list of blocks that was created & locked by lock_result_data
Query_cache_block * block = * result_block ;
2001-12-02 13:34:01 +01:00
do
{
2001-12-05 12:03:00 +01:00
Query_cache_block * current = block ;
2001-12-02 13:34:01 +01:00
block = block - > next ;
free_memory_block ( current ) ;
2001-12-05 12:03:00 +01:00
} while ( block ! = * result_block ) ;
* result_block = 0 ;
2001-12-02 13:34:01 +01:00
/*
2001-12-05 12:03:00 +01:00
It is not success = > not unlock structure_guard_mutex ( we need it to
free query )
2001-12-02 13:34:01 +01:00
*/
}
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " success %d " , ( int ) success ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( success ) ;
}
2002-01-12 14:40:52 +01:00
inline ulong Query_cache : : get_min_first_result_data_size ( )
{
if ( queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER )
return min_result_data_size ;
ulong avg_result = ( query_cache_size - free_memory ) / queries_in_cache ;
avg_result = min ( avg_result , query_cache_limit ) ;
return max ( min_result_data_size , avg_result ) ;
}
inline ulong Query_cache : : get_min_append_result_data_size ( )
{
return min_result_data_size ;
}
2001-12-05 12:03:00 +01:00
/*
Allocate one or more blocks to hold data
*/
my_bool Query_cache : : allocate_data_chain ( Query_cache_block * * result_block ,
2001-12-02 13:34:01 +01:00
ulong data_len ,
2002-01-12 14:40:52 +01:00
Query_cache_block * query_block ,
my_bool first_block )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
ulong all_headers_len = ( ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) ;
2001-12-02 13:34:01 +01:00
ulong len = data_len + all_headers_len ;
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::allocate_data_chain " ) ;
DBUG_PRINT ( " qcache " , ( " data_len %lu, all_headers_len %lu " ,
2001-12-02 13:34:01 +01:00
data_len , all_headers_len ) ) ;
2002-01-12 14:40:52 +01:00
ulong min_size = ( first_block ?
get_min_first_result_data_size ( ) :
get_min_append_result_data_size ( ) ) ;
* result_block = allocate_block ( max ( min_size , len ) ,
2001-12-05 12:03:00 +01:00
min_result_data_size = = 0 ,
all_headers_len + min_result_data_size ,
1 ) ;
my_bool success = ( * result_block ! = 0 ) ;
2001-12-02 13:34:01 +01:00
if ( success )
{
2001-12-05 12:03:00 +01:00
Query_cache_block * new_block = * result_block ;
new_block - > n_tables = 0 ;
new_block - > used = 0 ;
new_block - > type = Query_cache_block : : RES_INCOMPLETE ;
new_block - > next = new_block - > prev = new_block ;
Query_cache_result * header = new_block - > result ( ) ;
2001-12-02 13:34:01 +01:00
header - > parent ( query_block ) ;
2001-12-05 12:03:00 +01:00
if ( new_block - > length < len )
2001-12-02 13:34:01 +01:00
{
/*
2001-12-05 12:03:00 +01:00
We got less memory then we need ( no big memory blocks ) = >
Continue to allocated more blocks until we got everything we need .
2001-12-02 13:34:01 +01:00
*/
2001-12-05 12:03:00 +01:00
Query_cache_block * next_block ;
if ( ( success = allocate_data_chain ( & next_block ,
len - new_block - > length ,
2002-01-12 14:40:52 +01:00
query_block , first_block ) ) )
2001-12-05 12:03:00 +01:00
double_linked_list_join ( new_block , next_block ) ;
2001-12-02 13:34:01 +01:00
}
if ( success )
{
2001-12-05 12:03:00 +01:00
new_block - > used = min ( len , new_block - > length ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Block len %lu used %lu " ,
new_block - > length , new_block - > used ) ) ;
2001-12-02 13:34:01 +01:00
}
else
DBUG_PRINT ( " warning " , ( " Can't allocate block for continue " ) ) ;
}
else
DBUG_PRINT ( " warning " , ( " Can't allocate block for results " ) ) ;
DBUG_RETURN ( success ) ;
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Tables management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
Invalidate the first table in the table_list
*/
2001-12-02 13:34:01 +01:00
void Query_cache : : invalidate_table ( TABLE_LIST * table_list )
{
if ( table_list - > table ! = 0 )
2001-12-05 12:03:00 +01:00
invalidate_table ( table_list - > table ) ; // Table is open
2001-12-02 13:34:01 +01:00
else
{
2001-12-05 12:03:00 +01:00
char key [ MAX_DBKEY_LENGTH ] ;
2001-12-02 13:34:01 +01:00
uint key_length ;
Query_cache_block * table_block ;
key_length = ( uint ) ( strmov ( strmov ( key , table_list - > db ) + 1 ,
table_list - > real_name ) - key ) + 1 ;
2001-12-05 12:03:00 +01:00
// We don't store temporary tables => no key_length+=4 ...
2001-12-02 13:34:01 +01:00
if ( ( table_block = ( Query_cache_block * )
2001-12-09 23:08:24 +01:00
hash_search ( & tables , ( byte * ) key , key_length ) ) )
2001-12-02 13:34:01 +01:00
invalidate_table ( table_block ) ;
}
}
void Query_cache : : invalidate_table ( TABLE * table )
{
Query_cache_block * table_block ;
2001-12-05 12:03:00 +01:00
if ( ( table_block = ( ( Query_cache_block * )
2001-12-09 23:08:24 +01:00
hash_search ( & tables , ( byte * ) table - > table_cache_key ,
2001-12-05 12:03:00 +01:00
table - > key_length ) ) ) )
2001-12-02 13:34:01 +01:00
invalidate_table ( table_block ) ;
}
void Query_cache : : invalidate_table ( Query_cache_block * table_block )
{
2001-12-05 12:03:00 +01:00
Query_cache_block_table * list_root = table_block - > table ( 0 ) ;
while ( list_root - > next ! = list_root )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block = list_root - > next - > block ( ) ;
2001-12-02 13:34:01 +01:00
BLOCK_LOCK_WR ( query_block ) ;
free_query ( query_block ) ;
}
}
2001-12-05 12:03:00 +01:00
my_bool Query_cache : : register_all_tables ( Query_cache_block * block ,
TABLE_LIST * tables_used ,
2001-12-02 13:34:01 +01:00
TABLE_COUNTER_TYPE tables )
{
2001-12-05 12:03:00 +01:00
TABLE_COUNTER_TYPE n ;
DBUG_PRINT ( " qcache " , ( " register tables block 0x%lx, n %d, header %x " ,
( ulong ) block , ( int ) tables ,
( int ) ALIGN_SIZE ( sizeof ( Query_cache_block ) ) ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
Query_cache_block_table * block_table = block - > table ( 0 ) ;
for ( n = 0 ; tables_used ; tables_used = tables_used - > next , n + + , block_table + + )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " ,
2001-12-02 13:34:01 +01:00
( " table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx " ,
2001-12-05 12:03:00 +01:00
tables_used - > real_name , tables_used - > db ,
( ulong ) tables_used - > table ,
tables_used - > table - > key_length ,
( ulong ) tables_used - > table - > table_cache_key ) ) ;
2001-12-02 13:34:01 +01:00
block_table - > n = n ;
2001-12-05 12:03:00 +01:00
if ( ! insert_table ( tables_used - > table - > key_length ,
tables_used - > table - > table_cache_key , block_table ,
Query_cache_table : : type_convertion ( tables_used - > table - >
db_type ) ) )
2001-12-02 13:34:01 +01:00
break ;
2001-12-05 12:03:00 +01:00
if ( tables_used - > table - > db_type = = DB_TYPE_MRG_MYISAM )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
ha_myisammrg * handler = ( ha_myisammrg * ) tables_used - > table - > file ;
2001-12-02 13:34:01 +01:00
MYRG_INFO * file = handler - > myrg_info ( ) ;
2001-12-05 12:03:00 +01:00
for ( MYRG_TABLE * table = file - > open_tables ;
table ! = file - > end_table ;
table + + )
2001-12-02 13:34:01 +01:00
{
char key [ MAX_DBKEY_LENGTH ] ;
2001-12-05 12:03:00 +01:00
uint key_length = filename_2_table_key ( key , table - > table - > filename ) ;
( + + block_table ) - > n = + + n ;
2001-12-02 13:34:01 +01:00
if ( ! insert_table ( key_length , key , block_table ,
Query_cache_table : : type_convertion ( DB_TYPE_MYISAM ) ) )
goto err ;
}
}
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
err :
2001-12-05 12:03:00 +01:00
if ( tables_used )
{
DBUG_PRINT ( " qcache " , ( " failed at table %d " , ( int ) n ) ) ;
/* Unlink the tables we allocated above */
for ( Query_cache_block_table * tmp = block - > table ( 0 ) ;
tmp ! = block_table ;
tmp + + )
unlink_table ( tmp ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
return ( tables_used = = 0 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
/*
Insert used tablename in cache
Returns 0 on error
*/
my_bool
Query_cache : : insert_table ( uint key_len , char * key ,
Query_cache_block_table * node ,
Query_cache_table : : query_cache_table_type type )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::insert_table " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " insert table node 0x%lx, len %d " ,
2001-12-02 13:34:01 +01:00
( ulong ) node , key_len ) ) ;
2001-12-05 12:03:00 +01:00
Query_cache_block * table_block = ( ( Query_cache_block * )
2001-12-09 23:08:24 +01:00
hash_search ( & tables , ( byte * ) key ,
key_len ) ) ;
2001-12-02 13:34:01 +01:00
if ( table_block = = 0 )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " new table block from 0x%lx (%u) " ,
( ulong ) key , ( int ) key_len ) ) ;
2001-12-02 13:34:01 +01:00
table_block = write_block_data ( key_len , ( gptr ) key ,
ALIGN_SIZE ( sizeof ( Query_cache_table ) ) ,
Query_cache_block : : TABLE ,
1 , 1 ) ;
if ( table_block = = 0 )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Can't write table name to cache " ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
Query_cache_table * header = table_block - > table ( ) ;
2001-12-02 13:34:01 +01:00
header - > type ( type ) ;
double_linked_list_simple_include ( table_block ,
2001-12-05 12:03:00 +01:00
& tables_blocks [ type ] ) ;
Query_cache_block_table * list_root = table_block - > table ( 0 ) ;
2001-12-02 13:34:01 +01:00
list_root - > n = 0 ;
list_root - > next = list_root - > prev = list_root ;
2001-12-05 12:03:00 +01:00
if ( hash_insert ( & tables , ( const byte * ) table_block ) )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Can't insert table to hash " ) ) ;
2001-12-02 13:34:01 +01:00
// write_block_data return locked block
free_memory_block ( table_block ) ;
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
char * db = header - > db ( ) ;
2001-12-02 13:34:01 +01:00
header - > table ( db + strlen ( db ) + 1 ) ;
}
2001-12-05 12:03:00 +01:00
Query_cache_block_table * list_root = table_block - > table ( 0 ) ;
node - > next = list_root - > next ;
list_root - > next = node ;
node - > next - > prev = node ;
node - > prev = list_root ;
2001-12-02 13:34:01 +01:00
node - > parent = table_block - > table ( ) ;
DBUG_RETURN ( 1 ) ;
}
2001-12-05 12:03:00 +01:00
void Query_cache : : unlink_table ( Query_cache_block_table * node )
2001-12-02 13:34:01 +01:00
{
2001-12-09 23:08:24 +01:00
DBUG_ENTER ( " Query_cache::unlink_table " ) ;
2001-12-05 12:03:00 +01:00
node - > prev - > next = node - > next ;
node - > next - > prev = node - > prev ;
Query_cache_block_table * neighbour = node - > next ;
2001-12-02 13:34:01 +01:00
if ( neighbour - > next = = neighbour )
{
2001-12-05 12:03:00 +01:00
// list is empty (neighbor is root of list)
Query_cache_block * table_block = neighbour - > block ( ) ;
2001-12-02 13:34:01 +01:00
double_linked_list_exclude ( table_block ,
2001-12-05 12:03:00 +01:00
& tables_blocks [ table_block - > table ( ) - > type ( ) ] ) ;
2001-12-02 13:34:01 +01:00
hash_delete ( & tables , ( byte * ) table_block ) ;
free_memory_block ( table_block ) ;
}
2001-12-09 23:08:24 +01:00
DBUG_VOID_RETURN ;
2001-12-02 13:34:01 +01:00
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Free memory management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
Query_cache_block *
Query_cache : : allocate_block ( ulong len , my_bool not_less , ulong min ,
my_bool under_guard )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::allocate_n_lock_block " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " len %lu, not less %d, min %lu, uder_guard %d " ,
2001-12-02 13:34:01 +01:00
len , not_less , min , under_guard ) ) ;
if ( len > = min ( query_cache_size , query_cache_limit ) )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Query cache hase only %lu memory and limit %lu " ,
2001-12-02 13:34:01 +01:00
query_cache_size , query_cache_limit ) ) ;
DBUG_RETURN ( 0 ) ; // in any case we don't have such piece of memory
}
2001-12-05 12:03:00 +01:00
if ( ! under_guard )
STRUCT_LOCK ( & structure_guard_mutex ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
/* Free old queries until we have enough memory to store this block */
Query_cache_block * block ;
do
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
block = get_free_block ( len , not_less , min ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
while ( block = = 0 & & ! free_old_query ( ) ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
if ( block ! = 0 ) // If we found a suitable block
2001-12-02 13:34:01 +01:00
{
2002-01-12 14:40:52 +01:00
if ( block - > length > = ALIGN_SIZE ( len ) + min_allocation_unit )
2001-12-02 13:34:01 +01:00
split_block ( block , ALIGN_SIZE ( len ) ) ;
}
2001-12-05 12:03:00 +01:00
if ( ! under_guard )
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( block ) ;
}
2001-12-05 12:03:00 +01:00
Query_cache_block *
Query_cache : : get_free_block ( ulong len , my_bool not_less , ulong min )
2001-12-02 13:34:01 +01:00
{
Query_cache_block * block = 0 , * first = 0 ;
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::get_free_block " ) ;
DBUG_PRINT ( " qcache " , ( " length %lu, not_less %d, min %lu " , len ,
2001-12-02 13:34:01 +01:00
( int ) not_less , min ) ) ;
2001-12-05 12:03:00 +01:00
/* Find block with minimal size > len */
2001-12-02 13:34:01 +01:00
uint start = find_bin ( len ) ;
// try matching bin
if ( bins [ start ] . number ! = 0 )
{
2001-12-05 12:03:00 +01:00
Query_cache_block * list = bins [ start ] . free_blocks ;
2001-12-17 02:02:58 +01:00
ulong max_len = list - > prev - > length ;
if ( list - > prev - > length > = len ) // check block with max size
{
first = list ;
uint n = 0 ;
while ( n < QUERY_CACHE_MEM_BIN_TRY & &
first - > length < len ) //we don't need irst->next != list
{
first = first - > next ;
n + + ;
}
if ( first - > length > = len )
block = first ;
else // we don't need if (first->next != list)
{
n = 0 ;
block = list - > prev ;
while ( n < QUERY_CACHE_MEM_BIN_TRY & &
block - > length > len )
{
block = block - > prev ;
n + + ;
}
if ( block - > length < len )
block = block - > next ;
}
}
else
first = list - > prev ;
2001-12-02 13:34:01 +01:00
}
if ( block = = 0 & & start > 0 )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Try bins with bigger block size " ) ) ;
// Try more big bins
2001-12-02 13:34:01 +01:00
int i = start - 1 ;
2001-12-05 12:03:00 +01:00
while ( i > 0 & & bins [ i ] . number = = 0 )
2001-12-02 13:34:01 +01:00
i - - ;
if ( bins [ i ] . number > 0 )
block = bins [ i ] . free_blocks ;
}
2001-12-05 12:03:00 +01:00
// If no big blocks => try less size (if it is possible)
2001-12-02 13:34:01 +01:00
if ( block = = 0 & & ! not_less )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " Try to allocate a smaller block " ) ) ;
2001-12-02 13:34:01 +01:00
if ( first ! = 0 & & first - > length > min )
block = first ;
else
{
uint i = start + 1 ;
2001-12-05 12:03:00 +01:00
/* bins[mem_bin_num].number contains 1 for easy end test */
for ( i = start + 1 ; bins [ i ] . number = = 0 ; i + + ) ;
if ( i < mem_bin_num & & bins [ i ] . free_blocks - > prev - > length > = min )
2001-12-02 13:34:01 +01:00
block = bins [ i ] . free_blocks - > prev ;
}
}
if ( block ! = 0 )
exclude_from_free_memory_list ( block ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " getting block 0x%lx " , ( ulong ) block ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( block ) ;
}
2001-12-05 12:03:00 +01:00
void Query_cache : : free_memory_block ( Query_cache_block * block )
2001-12-02 13:34:01 +01:00
{
2001-12-13 01:31:19 +01:00
DBUG_ENTER ( " Query_cache::free_memory_block " ) ;
2001-12-02 13:34:01 +01:00
block - > used = 0 ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx " ,
2001-12-02 13:34:01 +01:00
( ulong ) first_block , ( ulong ) block , block - > pnext ,
( ulong ) block - > pprev ) ) ;
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
if ( block - > pnext ! = first_block & & block - > pnext - > is_free ( ) )
block = join_free_blocks ( block , block - > pnext ) ;
if ( block ! = first_block & & block - > pprev - > is_free ( ) )
block = join_free_blocks ( block - > pprev , block - > pprev ) ;
insert_into_free_memory_list ( block ) ;
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2002-01-12 14:40:52 +01:00
void Query_cache : : split_block ( Query_cache_block * block , ulong len )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::split_block " ) ;
2001-12-05 12:03:00 +01:00
Query_cache_block * new_block = ( Query_cache_block * ) ( ( ( byte * ) block ) + len ) ;
2001-12-02 13:34:01 +01:00
new_block - > init ( block - > length - len ) ;
2001-12-09 23:08:24 +01:00
total_blocks + + ;
2001-12-02 13:34:01 +01:00
block - > length = len ;
2001-12-05 12:03:00 +01:00
new_block - > pnext = block - > pnext ;
block - > pnext = new_block ;
new_block - > pprev = block ;
new_block - > pnext - > pprev = new_block ;
2001-12-02 13:34:01 +01:00
2002-01-12 14:40:52 +01:00
if ( block - > type = = Query_cache_block : : FREE )
// if block was free then it already joined with all free neighbours
insert_into_free_memory_list ( new_block ) ;
else
free_memory_block ( new_block ) ;
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " split 0x%lx (%lu) new 0x%lx " ,
2001-12-02 13:34:01 +01:00
( ulong ) block , len , ( ulong ) new_block ) ) ;
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
Query_cache_block *
Query_cache : : join_free_blocks ( Query_cache_block * first_block ,
2001-12-05 12:03:00 +01:00
Query_cache_block * block_in_list )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block * second_block ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::join_free_blocks " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " ,
2001-12-02 13:34:01 +01:00
( " join first 0x%lx, pnext 0x%lx, in list 0x%lx " ,
( ulong ) first_block , ( ulong ) first_block - > pnext ,
( ulong ) block_in_list ) ) ;
2001-12-05 12:03:00 +01:00
exclude_from_free_memory_list ( block_in_list ) ;
second_block = first_block - > pnext ;
// May be was not free block
second_block - > used = 0 ;
2001-12-02 13:34:01 +01:00
second_block - > destroy ( ) ;
2001-12-09 23:08:24 +01:00
total_blocks - - ;
2001-12-02 13:34:01 +01:00
first_block - > length + = second_block - > length ;
first_block - > pnext = second_block - > pnext ;
second_block - > pnext - > pprev = first_block ;
DBUG_RETURN ( first_block ) ;
}
2001-12-05 12:03:00 +01:00
my_bool Query_cache : : append_next_free_block ( Query_cache_block * block ,
2001-12-02 13:34:01 +01:00
ulong add_size )
{
2001-12-05 12:03:00 +01:00
Query_cache_block * next_block = block - > pnext ;
2001-12-02 13:34:01 +01:00
DBUG_ENTER ( " Query_cache::append_next_free_block " ) ;
DBUG_PRINT ( " enter " , ( " block 0x%lx, add_size %lu " , ( ulong ) block ,
add_size ) ) ;
2001-12-05 12:03:00 +01:00
2001-12-13 01:31:19 +01:00
if ( next_block ! = first_block & & next_block - > is_free ( ) )
2001-12-02 13:34:01 +01:00
{
ulong old_len = block - > length ;
2001-12-05 12:03:00 +01:00
exclude_from_free_memory_list ( next_block ) ;
2001-12-02 13:34:01 +01:00
next_block - > destroy ( ) ;
2001-12-09 23:08:24 +01:00
total_blocks - - ;
2001-12-02 13:34:01 +01:00
block - > length + = next_block - > length ;
block - > pnext = next_block - > pnext ;
next_block - > pnext - > pprev = block ;
2001-12-05 12:03:00 +01:00
if ( block - > length > ALIGN_SIZE ( old_len + add_size ) + min_allocation_unit )
2001-12-02 13:34:01 +01:00
split_block ( block , ALIGN_SIZE ( old_len + add_size ) ) ;
DBUG_PRINT ( " exit " , ( " block was appended " ) ) ;
DBUG_RETURN ( 1 ) ;
}
2001-12-05 12:03:00 +01:00
DBUG_RETURN ( 0 ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
void Query_cache : : exclude_from_free_memory_list ( Query_cache_block * free_block )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::exclude_from_free_memory_list " ) ;
2001-12-05 12:03:00 +01:00
Query_cache_memory_bin * bin = * ( ( Query_cache_memory_bin * * )
free_block - > data ( ) ) ;
double_linked_list_exclude ( free_block , & bin - > free_blocks ) ;
2001-12-02 13:34:01 +01:00
bin - > number - - ;
free_memory - = free_block - > length ;
2001-12-09 23:08:24 +01:00
free_memory_blocks - - ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " exclude block 0x%lx, bin 0x%lx " , ( ulong ) free_block ,
( ulong ) bin ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
void Query_cache : : insert_into_free_memory_list ( Query_cache_block * free_block )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::insert_into_free_memory_list " ) ;
uint idx = find_bin ( free_block - > length ) ;
2001-12-05 12:03:00 +01:00
insert_into_free_memory_sorted_list ( free_block , & bins [ idx ] . free_blocks ) ;
2001-12-02 13:34:01 +01:00
/*
2001-12-05 12:03:00 +01:00
We have enough memory in block for storing bin reference due to
2001-12-02 13:34:01 +01:00
min_allocation_unit choice
*/
2001-12-05 12:03:00 +01:00
Query_cache_memory_bin * * bin_ptr = ( ( Query_cache_memory_bin * * )
free_block - > data ( ) ) ;
* bin_ptr = bins + idx ;
( * bin_ptr ) - > number + + ;
DBUG_PRINT ( " qcache " , ( " insert block 0x%lx, bin[%d] 0x%lx " ,
( ulong ) free_block , idx , ( ulong ) * bin_ptr ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
uint Query_cache : : find_bin ( ulong size )
{
DBUG_ENTER ( " Query_cache::find_bin " ) ;
2001-12-17 02:02:58 +01:00
// Binary search
int left = 0 , right = mem_bin_steps ;
do
{
int middle = ( left + right ) / 2 ;
if ( steps [ middle ] . size > size )
left = middle + 1 ;
else
right = middle ;
} while ( left < right ) ;
if ( left = = 0 )
2001-12-02 13:34:01 +01:00
{
// first bin not subordinate of common rules
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " first bin (# 0), size %lu " , size ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( 0 ) ;
}
2001-12-17 02:02:58 +01:00
uint bin = steps [ left ] . idx -
( uint ) ( ( size - steps [ left ] . size ) / steps [ left ] . increment ) ;
2001-12-17 03:16:10 +01:00
# ifndef DBUG_OFF
2001-12-17 02:02:58 +01:00
bins_dump ( ) ;
2001-12-17 03:16:10 +01:00
# endif
2001-12-17 02:02:58 +01:00
DBUG_PRINT ( " qcache " , ( " bin %u step %u, size %lu step size %lu " ,
bin , left , size , steps [ left ] . size ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( bin ) ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Lists management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
void Query_cache : : move_to_query_list_end ( Query_cache_block * query_block )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::move_to_query_list_end " ) ;
2001-12-05 12:03:00 +01:00
double_linked_list_exclude ( query_block , & queries_blocks ) ;
double_linked_list_simple_include ( query_block , & queries_blocks ) ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : insert_into_free_memory_sorted_list ( Query_cache_block *
new_block ,
2001-12-05 12:03:00 +01:00
Query_cache_block * *
list )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::insert_into_free_memory_sorted_list " ) ;
/*
list sorted by size in ascendant order , because we need small blocks
2001-12-05 12:03:00 +01:00
more frequently than bigger ones
2001-12-02 13:34:01 +01:00
*/
new_block - > used = 0 ;
new_block - > n_tables = 0 ;
new_block - > type = Query_cache_block : : FREE ;
2001-12-05 12:03:00 +01:00
if ( * list = = 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
* list = new_block - > next = new_block - > prev = new_block ;
DBUG_PRINT ( " qcache " , ( " inserted into empty list " ) ) ;
2001-12-02 13:34:01 +01:00
}
else
{
2001-12-05 12:03:00 +01:00
Query_cache_block * point = * list ;
2001-12-02 13:34:01 +01:00
if ( point - > length > = new_block - > length )
{
point = point - > prev ;
2001-12-05 12:03:00 +01:00
* list = new_block ;
2001-12-02 13:34:01 +01:00
}
else
{
2001-12-05 12:03:00 +01:00
/* Find right position in sorted list to put block */
while ( point - > next ! = * list & &
point - > next - > length < new_block - > length )
2001-12-02 13:34:01 +01:00
point = point - > next ;
}
2001-12-05 12:03:00 +01:00
new_block - > prev = point ;
new_block - > next = point - > next ;
new_block - > next - > prev = new_block ;
point - > next = new_block ;
2001-12-02 13:34:01 +01:00
}
free_memory + = new_block - > length ;
2001-12-09 23:08:24 +01:00
free_memory_blocks + + ;
2001-12-02 13:34:01 +01:00
DBUG_VOID_RETURN ;
}
void
2001-12-05 12:03:00 +01:00
Query_cache : : double_linked_list_simple_include ( Query_cache_block * point ,
Query_cache_block * *
list_pointer )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::double_linked_list_simple_include " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " including block 0x%lx " , ( ulong ) point ) ) ;
if ( * list_pointer = = 0 )
* list_pointer = point - > next = point - > prev = point ;
2001-12-02 13:34:01 +01:00
else
{
2001-12-09 23:08:24 +01:00
// insert to and of list
2001-12-05 12:03:00 +01:00
point - > next = ( * list_pointer ) ;
point - > prev = ( * list_pointer ) - > prev ;
2001-12-02 13:34:01 +01:00
point - > prev - > next = point ;
2001-12-05 12:03:00 +01:00
( * list_pointer ) - > prev = point ;
2001-12-02 13:34:01 +01:00
}
DBUG_VOID_RETURN ;
}
void
Query_cache : : double_linked_list_exclude ( Query_cache_block * point ,
2001-12-05 12:03:00 +01:00
Query_cache_block * * list_pointer )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::double_linked_list_exclude " ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " excluding block 0x%lx, list 0x%lx " ,
2001-12-02 13:34:01 +01:00
( ulong ) point , ( ulong ) list_pointer ) ) ;
if ( point - > next = = point )
2001-12-05 12:03:00 +01:00
* list_pointer = 0 ; // empty list
2001-12-02 13:34:01 +01:00
else
{
point - > next - > prev = point - > prev ;
point - > prev - > next = point - > next ;
2001-12-05 12:03:00 +01:00
if ( point = = * list_pointer )
* list_pointer = point - > next ;
2001-12-02 13:34:01 +01:00
}
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
void Query_cache : : double_linked_list_join ( Query_cache_block * head_tail ,
Query_cache_block * tail_head )
{
Query_cache_block * head_head = head_tail - > next ,
2001-12-05 12:03:00 +01:00
* tail_tail = tail_head - > prev ;
2001-12-02 13:34:01 +01:00
head_head - > prev = tail_tail ;
head_tail - > next = tail_head ;
tail_head - > prev = head_tail ;
tail_tail - > next = head_head ;
}
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Query
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
/*
2001-12-22 14:13:31 +01:00
If query is cacheable return number tables in query
2001-12-05 12:03:00 +01:00
( query without tables are not cached )
2001-12-02 13:34:01 +01:00
*/
2001-12-05 12:03:00 +01:00
TABLE_COUNTER_TYPE Query_cache : : is_cacheable ( THD * thd , uint32 query_len ,
char * query ,
LEX * lex , TABLE_LIST * tables_used )
2001-12-02 13:34:01 +01:00
{
TABLE_COUNTER_TYPE tables = 0 ;
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::is_cacheable " ) ;
2001-12-02 13:34:01 +01:00
if ( lex - > sql_command = = SQLCOM_SELECT & &
thd - > temporary_tables = = 0 & &
2001-12-05 12:03:00 +01:00
( thd - > query_cache_type = = 1 | |
( thd - > query_cache_type = = 2 & & ( lex - > select - > options &
2001-12-02 13:34:01 +01:00
OPTION_TO_QUERY_CACHE ) ) ) & &
thd - > safe_to_cache_query )
{
2001-12-05 12:03:00 +01:00
my_bool has_transactions = 0 ;
DBUG_PRINT ( " qcache " , ( " options %lx %lx, type %u " ,
2001-12-02 13:34:01 +01:00
OPTION_TO_QUERY_CACHE ,
lex - > select - > options ,
( int ) thd - > query_cache_type ) ) ;
2001-12-05 12:03:00 +01:00
for ( ; tables_used ; tables_used = tables_used - > next )
2001-12-02 13:34:01 +01:00
{
tables + + ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " table %s, db %s, type %u " ,
tables_used - > real_name ,
tables_used - > db , tables_used - > table - > db_type ) ) ;
2001-12-02 13:34:01 +01:00
has_transactions = ( has_transactions | |
2001-12-05 12:03:00 +01:00
tables_used - > table - > file - > has_transactions ( ) ) ;
if ( tables_used - > table - > db_type = = DB_TYPE_MRG_ISAM )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " select not cacheable: used MRG_ISAM table(s) " ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
if ( tables_used - > table - > db_type = = DB_TYPE_MRG_MYISAM )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
ha_myisammrg * handler = ( ha_myisammrg * ) tables_used - > table - > file ;
2001-12-02 13:34:01 +01:00
MYRG_INFO * file = handler - > myrg_info ( ) ;
2001-12-05 12:03:00 +01:00
tables + = ( file - > end_table - file - > open_tables ) ;
2001-12-02 13:34:01 +01:00
}
}
if ( ( thd - > options & ( OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN ) ) & &
has_transactions )
{
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " not in autocommin mode " ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " select is using %d tables " , tables ) ) ;
DBUG_RETURN ( tables ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " ,
( " not interesting query: %d or not cacheable, options %lx %lx, type %u " ,
2001-12-02 13:34:01 +01:00
( int ) lex - > sql_command ,
OPTION_TO_QUERY_CACHE ,
lex - > select - > options ,
( int ) thd - > query_cache_type ) ) ;
DBUG_RETURN ( 0 ) ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
/*****************************************************************************
2001-12-05 12:03:00 +01:00
Packing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-02 13:34:01 +01:00
void Query_cache : : pack_cache ( )
{
2002-01-05 21:51:42 +01:00
DBUG_ENTER ( " Query_cache::pack_cache " ) ;
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
2002-01-05 21:51:42 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 1 ) ; ) ;
2001-12-05 12:03:00 +01:00
byte * border = 0 ;
Query_cache_block * before = 0 ;
2001-12-02 13:34:01 +01:00
ulong gap = 0 ;
my_bool ok = 1 ;
2001-12-06 00:05:30 +01:00
Query_cache_block * block = first_block ;
2001-12-05 12:03:00 +01:00
DUMP ( this ) ;
2001-12-06 00:05:30 +01:00
if ( first_block )
2001-12-02 13:34:01 +01:00
{
2001-12-06 00:05:30 +01:00
do
{
2001-12-19 14:45:04 +01:00
Query_cache_block * next = block - > pnext ;
2001-12-06 00:05:30 +01:00
ok = move_by_type ( & border , & before , & gap , block ) ;
2001-12-19 14:45:04 +01:00
block = next ;
2001-12-06 00:05:30 +01:00
} while ( ok & & block ! = first_block ) ;
2001-12-05 12:03:00 +01:00
2001-12-06 00:05:30 +01:00
if ( border ! = 0 )
{
Query_cache_block * new_block = ( Query_cache_block * ) border ;
new_block - > init ( gap ) ;
2001-12-09 23:08:24 +01:00
total_blocks + + ;
2001-12-06 00:05:30 +01:00
new_block - > pnext = before - > pnext ;
before - > pnext = new_block ;
new_block - > pprev = before ;
new_block - > pnext - > pprev = new_block ;
insert_into_free_memory_list ( new_block ) ;
}
DUMP ( this ) ;
2001-12-02 13:34:01 +01:00
}
2002-01-05 21:51:42 +01:00
DBUG_EXECUTE ( " check_querycache " , query_cache . check_integrity ( 1 ) ; ) ;
2001-12-02 13:34:01 +01:00
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
DBUG_VOID_RETURN ;
}
2001-12-05 12:03:00 +01:00
my_bool Query_cache : : move_by_type ( byte * * border ,
Query_cache_block * * before , ulong * gap ,
Query_cache_block * block )
2001-12-02 13:34:01 +01:00
{
DBUG_ENTER ( " Query_cache::move_by_type " ) ;
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
my_bool ok = 1 ;
2001-12-05 12:03:00 +01:00
switch ( block - > type ) {
2001-12-02 13:34:01 +01:00
case Query_cache_block : : FREE :
2001-12-05 12:03:00 +01:00
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx FREE " , ( ulong ) block ) ) ;
if ( * border = = 0 )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
* border = ( byte * ) block ;
* before = block - > pprev ;
DBUG_PRINT ( " qcache " , ( " gap beginning here " ) ) ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
exclude_from_free_memory_list ( block ) ;
* gap + = block - > length ;
block - > pprev - > pnext = block - > pnext ;
block - > pnext - > pprev = block - > pprev ;
block - > destroy ( ) ;
2001-12-09 23:08:24 +01:00
total_blocks - - ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " added to gap (%lu) " , * gap ) ) ;
break ;
}
2001-12-02 13:34:01 +01:00
case Query_cache_block : : TABLE :
2001-12-05 12:03:00 +01:00
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx TABLE " , ( ulong ) block ) ) ;
if ( * border = = 0 )
2001-12-02 13:34:01 +01:00
break ;
2001-12-05 12:03:00 +01:00
ulong len = block - > length , used = block - > used ;
Query_cache_block_table * list_root = block - > table ( 0 ) ;
Query_cache_block_table * tprev = list_root - > prev ,
* tnext = list_root - > next ;
Query_cache_block * prev = block - > prev ,
* next = block - > next ,
* pprev = block - > pprev ,
* pnext = block - > pnext ,
* new_block = ( Query_cache_block * ) * border ;
2002-01-03 18:04:01 +01:00
uint tablename_offset = block - > table ( ) - > table ( ) - block - > table ( ) - > db ( ) ;
2001-12-05 12:03:00 +01:00
char * data = ( char * ) block - > data ( ) ;
byte * key ;
uint key_length ;
2001-12-13 01:31:19 +01:00
key = query_cache_table_get_key ( ( byte * ) block , & key_length , 0 ) ;
2001-12-09 23:08:24 +01:00
hash_search ( & tables , ( byte * ) key , key_length ) ;
2001-12-05 12:03:00 +01:00
block - > destroy ( ) ;
new_block - > init ( len ) ;
new_block - > type = Query_cache_block : : TABLE ;
new_block - > used = used ;
new_block - > n_tables = 1 ;
2002-01-03 18:04:01 +01:00
memmove ( ( char * ) new_block - > data ( ) , data , len - new_block - > headers_len ( ) ) ;
2001-12-05 12:03:00 +01:00
relink ( block , new_block , next , prev , pnext , pprev ) ;
if ( tables_blocks [ new_block - > table ( ) - > type ( ) ] = = block )
tables_blocks [ new_block - > table ( ) - > type ( ) ] = new_block ;
Query_cache_block_table * nlist_root = new_block - > table ( 0 ) ;
nlist_root - > n = 0 ;
2002-01-05 21:51:42 +01:00
nlist_root - > next = tnext ;
tnext - > prev = nlist_root ;
nlist_root - > prev = tprev ;
tprev - > next = nlist_root ;
DBUG_PRINT ( " qcache " ,
( " list_root: 0x%lx tnext 0x%lx tprev 0x%lx tprev->next 0x%lx tnext->prev 0x%lx " ,
( ulong ) list_root , ( ulong ) tnext , ( ulong ) tprev ,
( ulong ) tprev - > next , ( ulong ) tnext - > prev ) ) ;
2002-01-03 18:04:01 +01:00
/*
Go through all queries that uses this table and change them to
point to the new table object
*/
Query_cache_table * new_block_table = new_block - > table ( ) ;
2001-12-09 23:08:24 +01:00
for ( ; tnext ! = nlist_root ; tnext = tnext - > next )
2002-01-03 18:04:01 +01:00
tnext - > parent = new_block_table ;
2001-12-05 12:03:00 +01:00
* border + = len ;
* before = new_block ;
2002-01-03 18:04:01 +01:00
/* Fix pointer to table name */
new_block - > table ( ) - > table ( new_block - > table ( ) - > db ( ) + tablename_offset ) ;
2001-12-05 12:03:00 +01:00
/* Fix hash to point at moved block */
hash_replace ( & tables , tables . current_record , ( byte * ) new_block ) ;
DBUG_PRINT ( " qcache " , ( " moved %lu bytes to 0x%lx, new gap at 0x%lx " ,
len , ( ulong ) new_block , ( ulong ) * border ) ) ;
break ;
}
2001-12-02 13:34:01 +01:00
case Query_cache_block : : QUERY :
2001-12-05 12:03:00 +01:00
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx QUERY " , ( ulong ) block ) ) ;
if ( * border = = 0 )
break ;
BLOCK_LOCK_WR ( block ) ;
ulong len = block - > length , used = block - > used ;
TABLE_COUNTER_TYPE n_tables = block - > n_tables ;
Query_cache_block * prev = block - > prev ,
* next = block - > next ,
* pprev = block - > pprev ,
* pnext = block - > pnext ,
* new_block = ( Query_cache_block * ) * border ;
char * data = ( char * ) block - > data ( ) ;
Query_cache_block * first_result_block = ( ( Query_cache_query * )
block - > data ( ) ) - > result ( ) ;
byte * key ;
uint key_length ;
2001-12-13 01:31:19 +01:00
key = query_cache_query_get_key ( ( byte * ) block , & key_length , 0 ) ;
2001-12-09 23:08:24 +01:00
hash_search ( & queries , ( byte * ) key , key_length ) ;
2002-01-03 18:04:01 +01:00
// Move table of used tables
memmove ( ( char * ) new_block - > table ( 0 ) , ( char * ) block - > table ( 0 ) ,
2001-12-05 12:03:00 +01:00
ALIGN_SIZE ( n_tables * sizeof ( Query_cache_block_table ) ) ) ;
block - > query ( ) - > unlock_n_destroy ( ) ;
block - > destroy ( ) ;
new_block - > init ( len ) ;
new_block - > type = Query_cache_block : : QUERY ;
new_block - > used = used ;
new_block - > n_tables = n_tables ;
2002-01-03 18:04:01 +01:00
memmove ( ( char * ) new_block - > data ( ) , data , len - new_block - > headers_len ( ) ) ;
2001-12-05 12:03:00 +01:00
relink ( block , new_block , next , prev , pnext , pprev ) ;
if ( queries_blocks = = block )
queries_blocks = new_block ;
for ( TABLE_COUNTER_TYPE j = 0 ; j < n_tables ; j + + )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
Query_cache_block_table * block_table = new_block - > table ( j ) ;
block_table - > next - > prev = block_table ;
block_table - > prev - > next = block_table ;
}
DBUG_PRINT ( " qcache " , ( " after circle tt " ) ) ;
* border + = len ;
* before = new_block ;
new_block - > query ( ) - > result ( first_result_block ) ;
if ( first_result_block ! = 0 )
{
Query_cache_block * result_block = first_result_block ;
do
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
result_block - > result ( ) - > parent ( new_block ) ;
result_block = result_block - > next ;
} while ( result_block ! = first_result_block ) ;
}
2001-12-17 02:02:58 +01:00
Query_cache_query * new_query = ( ( Query_cache_query * ) new_block - > data ( ) ) ;
2002-01-02 20:29:41 +01:00
sem_init ( & new_query - > lock , 0 , 1 ) ;
2001-12-17 02:02:58 +01:00
pthread_mutex_init ( & new_query - > clients_guard , MY_MUTEX_INIT_FAST ) ;
2001-12-22 14:13:31 +01:00
2001-12-23 01:43:46 +01:00
/*
If someone is writing to this block , inform the writer that the block
has been moved .
*/
2001-12-05 12:03:00 +01:00
NET * net = new_block - > query ( ) - > writer ( ) ;
if ( net ! = 0 )
{
2001-12-22 14:13:31 +01:00
net - > query_cache_query = ( gptr ) new_block ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
/* Fix hash to point at moved block */
hash_replace ( & queries , queries . current_record , ( byte * ) new_block ) ;
DBUG_PRINT ( " qcache " , ( " moved %lu bytes to 0x%lx, new gap at 0x%lx " ,
len , ( ulong ) new_block , ( ulong ) * border ) ) ;
break ;
}
2001-12-02 13:34:01 +01:00
case Query_cache_block : : RES_INCOMPLETE :
case Query_cache_block : : RES_BEG :
case Query_cache_block : : RES_CONT :
case Query_cache_block : : RESULT :
2001-12-05 12:03:00 +01:00
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx RES* (%d) " , ( ulong ) block ,
( int ) block - > type ) ) ;
if ( * border = = 0 )
2001-12-02 13:34:01 +01:00
break ;
2001-12-05 12:03:00 +01:00
Query_cache_block * query_block = block - > result ( ) - > parent ( ) ,
* next = block - > next ,
* prev = block - > prev ;
Query_cache_block : : block_type type = block - > type ;
BLOCK_LOCK_WR ( query_block ) ;
ulong len = block - > length , used = block - > used ;
Query_cache_block * pprev = block - > pprev ,
* pnext = block - > pnext ,
* new_block = ( Query_cache_block * ) * border ;
char * data = ( char * ) block - > data ( ) ;
block - > destroy ( ) ;
new_block - > init ( len ) ;
new_block - > type = type ;
new_block - > used = used ;
2002-01-03 18:04:01 +01:00
memmove ( ( char * ) new_block - > data ( ) , data , len - new_block - > headers_len ( ) ) ;
2001-12-05 12:03:00 +01:00
relink ( block , new_block , next , prev , pnext , pprev ) ;
new_block - > result ( ) - > parent ( query_block ) ;
Query_cache_query * query = query_block - > query ( ) ;
if ( query - > result ( ) = = block )
query - > result ( new_block ) ;
* border + = len ;
* before = new_block ;
/* If result writing complete && we have free space in block */
ulong free_space = new_block - > length - new_block - > used ;
if ( query - > result ( ) - > type = = Query_cache_block : : RESULT & &
new_block - > length > new_block - > used & &
* gap + free_space > min_allocation_unit & &
new_block - > length - free_space > min_allocation_unit )
{
* border - = free_space ;
* gap + = free_space ;
new_block - > length - = free_space ;
2001-12-02 13:34:01 +01:00
}
2001-12-05 12:03:00 +01:00
BLOCK_UNLOCK_WR ( query_block ) ;
DBUG_PRINT ( " qcache " , ( " moved %lu bytes to 0x%lx, new gap at 0x%lx " ,
len , ( ulong ) new_block , ( ulong ) * border ) ) ;
break ;
}
2001-12-02 13:34:01 +01:00
default :
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " error " , ( " unexpected block type %d, block 0x%lx " ,
( int ) block - > type , ( ulong ) block ) ) ;
2001-12-02 13:34:01 +01:00
ok = 0 ;
}
DBUG_RETURN ( ok ) ;
}
2001-12-05 12:03:00 +01:00
void Query_cache : : relink ( Query_cache_block * oblock ,
Query_cache_block * nblock ,
Query_cache_block * next , Query_cache_block * prev ,
Query_cache_block * pnext , Query_cache_block * pprev )
2001-12-02 13:34:01 +01:00
{
2001-12-21 06:00:58 +01:00
if ( prev = = oblock ) //check pointer to himself
{
nblock - > prev = nblock ;
nblock - > next = nblock ;
}
else
{
nblock - > prev = prev ;
prev - > next = nblock ;
}
if ( next ! = oblock )
{
nblock - > next = next ;
next - > prev = nblock ;
}
2001-12-05 12:03:00 +01:00
nblock - > pprev = pprev ; // Physical pointer to himself have only 1 free block
2001-12-02 13:34:01 +01:00
nblock - > pnext = pnext ;
pprev - > pnext = nblock ;
pnext - > pprev = nblock ;
}
2001-12-05 12:03:00 +01:00
2001-12-02 13:34:01 +01:00
my_bool Query_cache : : join_results ( ulong join_limit )
{
my_bool has_moving = 0 ;
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::join_results " ) ;
2001-12-02 13:34:01 +01:00
STRUCT_LOCK ( & structure_guard_mutex ) ;
if ( queries_blocks ! = 0 )
{
2001-12-05 12:03:00 +01:00
Query_cache_block * block = queries_blocks ;
2001-12-02 13:34:01 +01:00
do
{
2001-12-05 12:03:00 +01:00
Query_cache_query * header = block - > query ( ) ;
2001-12-02 13:34:01 +01:00
if ( header - > result ( ) ! = 0 & &
header - > result ( ) - > type = = Query_cache_block : : RESULT & &
header - > length ( ) > join_limit )
{
Query_cache_block * new_result_block =
get_free_block ( header - > length ( ) +
ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) , 1 , 0 ) ;
if ( new_result_block ! = 0 )
{
has_moving = 1 ;
Query_cache_block * first_result = header - > result ( ) ;
2001-12-05 12:03:00 +01:00
ulong new_len = ( header - > length ( ) +
ALIGN_SIZE ( sizeof ( Query_cache_block ) ) +
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) ;
2001-12-02 13:34:01 +01:00
if ( new_result_block - > length >
ALIGN_SIZE ( new_len ) + min_allocation_unit )
2001-12-05 12:03:00 +01:00
split_block ( new_result_block , ALIGN_SIZE ( new_len ) ) ;
BLOCK_LOCK_WR ( block ) ;
2001-12-02 13:34:01 +01:00
header - > result ( new_result_block ) ;
new_result_block - > type = Query_cache_block : : RESULT ;
new_result_block - > n_tables = 0 ;
new_result_block - > used = new_len ;
new_result_block - > next = new_result_block - > prev = new_result_block ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " new block %lu/%lu (%lu) " ,
2001-12-02 13:34:01 +01:00
new_result_block - > length ,
new_result_block - > used ,
header - > length ( ) ) ) ;
Query_cache_result * new_result = new_result_block - > result ( ) ;
2001-12-05 12:03:00 +01:00
new_result - > parent ( block ) ;
2001-12-02 13:34:01 +01:00
byte * write_to = ( byte * ) new_result - > data ( ) ;
Query_cache_block * result_block = first_result ;
do
{
2001-12-05 12:03:00 +01:00
ulong len = ( result_block - > used - result_block - > headers_len ( ) -
ALIGN_SIZE ( sizeof ( Query_cache_result ) ) ) ;
2001-12-02 13:34:01 +01:00
DBUG_PRINT ( " loop " , ( " add block %lu/%lu (%lu) " ,
result_block - > length ,
result_block - > used ,
len ) ) ;
memcpy ( ( char * ) write_to ,
( char * ) result_block - > result ( ) - > data ( ) ,
len ) ;
write_to + = len ;
2001-12-05 12:03:00 +01:00
Query_cache_block * old_result_block = result_block ;
2001-12-02 13:34:01 +01:00
result_block = result_block - > next ;
free_memory_block ( old_result_block ) ;
} while ( result_block ! = first_result ) ;
2001-12-05 12:03:00 +01:00
BLOCK_UNLOCK_WR ( block ) ;
2001-12-02 13:34:01 +01:00
}
}
2001-12-05 12:03:00 +01:00
block = block - > next ;
} while ( block ! = queries_blocks ) ;
2001-12-02 13:34:01 +01:00
}
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
DBUG_RETURN ( has_moving ) ;
}
2001-12-05 12:03:00 +01:00
uint Query_cache : : filename_2_table_key ( char * key , const char * path )
2001-12-02 13:34:01 +01:00
{
2001-12-05 12:03:00 +01:00
char tablename [ FN_REFLEN + 2 ] , * filename , * dbname ;
uint db_length ;
DBUG_ENTER ( " Query_cache::filename_2_table_key " ) ;
/* Safety if filename didn't have a directory name */
tablename [ 0 ] = FN_LIBCHAR ;
tablename [ 1 ] = FN_LIBCHAR ;
/* Convert filename to this OS's format in tablename */
fn_format ( tablename + 2 , path , " " , " " , MY_REPLACE_EXT ) ;
filename = tablename + dirname_length ( tablename + 2 ) + 2 ;
/* Find start of databasename */
for ( dbname = filename - 2 ; dbname [ - 1 ] ! = FN_LIBCHAR ; dbname - - ) ;
2001-12-09 23:08:24 +01:00
db_length = ( filename - dbname ) - 1 ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " table '%-.*s.%s' " , db_length , dbname , filename ) ) ;
2001-12-06 00:05:30 +01:00
DBUG_RETURN ( ( uint ) ( strmov ( strmake ( key , dbname , db_length ) + 1 ,
2001-12-05 12:03:00 +01:00
filename ) - key ) + 1 ) ;
}
/****************************************************************************
Functions to be used when debugging
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-22 14:13:31 +01:00
# if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK)
void wreck ( uint line , const char * message ) { }
void bins_dump ( ) { }
void cache_dump ( ) { }
void queries_dump ( ) { }
void tables_dump ( ) { }
2002-01-03 18:04:01 +01:00
my_bool check_integrity ( bool not_locked ) { return 0 ; }
2001-12-22 14:13:31 +01:00
my_bool in_list ( Query_cache_block * root , Query_cache_block * point ,
const char * name ) { return 0 ; }
my_bool in_blocks ( Query_cache_block * point ) { return 0 ; }
# else
2001-12-05 12:03:00 +01:00
void Query_cache : : wreck ( uint line , const char * message )
{
2001-12-10 17:16:51 +01:00
THD * thd = current_thd ;
2001-12-05 12:03:00 +01:00
DBUG_ENTER ( " Query_cache::wreck " ) ;
query_cache_size = 0 ;
if ( * message )
DBUG_PRINT ( " error " , ( " %s " , message ) ) ;
DBUG_PRINT ( " warning " , ( " ================================== " ) ) ;
DBUG_PRINT ( " warning " , ( " %5d QUERY CACHE WRECK => DISABLED " , line ) ) ;
DBUG_PRINT ( " warning " , ( " ================================== " ) ) ;
2001-12-10 17:16:51 +01:00
if ( thd )
thd - > killed = 1 ;
2001-12-05 12:03:00 +01:00
cache_dump ( ) ;
2002-01-03 18:04:01 +01:00
/* check_integrity(0); */ /* Can't call it here because of locks */
2001-12-13 01:31:19 +01:00
bins_dump ( ) ;
2001-12-05 12:03:00 +01:00
DBUG_VOID_RETURN ;
}
void Query_cache : : bins_dump ( )
{
uint i ;
2002-01-02 16:55:10 +01:00
if ( ! initialized )
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
return ;
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " mem_bin_num=%u, mem_bin_steps=%u " ,
mem_bin_num , mem_bin_steps ) ) ;
DBUG_PRINT ( " qcache " , ( " ------------------------- " ) ) ;
DBUG_PRINT ( " qcache " , ( " size idx step " ) ) ;
DBUG_PRINT ( " qcache " , ( " ------------------------- " ) ) ;
for ( i = 0 ; i < mem_bin_steps ; i + + )
{
DBUG_PRINT ( " qcache " , ( " %10lu %3d %10lu " , steps [ i ] . size , steps [ i ] . idx ,
steps [ i ] . increment ) ) ;
}
DBUG_PRINT ( " qcache " , ( " ------------------------- " ) ) ;
DBUG_PRINT ( " qcache " , ( " size num " ) ) ;
DBUG_PRINT ( " qcache " , ( " ------------------------- " ) ) ;
for ( i = 0 ; i < mem_bin_num ; i + + )
{
DBUG_PRINT ( " qcache " , ( " %10lu %3d 0x%lx " , bins [ i ] . size , bins [ i ] . number ,
( ulong ) & ( bins [ i ] ) ) ) ;
if ( bins [ i ] . free_blocks )
{
Query_cache_block * block = bins [ i ] . free_blocks ;
do {
DBUG_PRINT ( " qcache " , ( " \\ -- %lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx " ,
block - > length , ( ulong ) block ,
( ulong ) block - > next , ( ulong ) block - > prev ,
( ulong ) block - > pnext , ( ulong ) block - > pprev ) ) ;
block = block - > next ;
} while ( block ! = bins [ i ] . free_blocks ) ;
}
}
DBUG_PRINT ( " qcache " , ( " ------------------------- " ) ) ;
}
void Query_cache : : cache_dump ( )
{
2002-01-02 16:55:10 +01:00
if ( ! initialized )
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
return ;
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " ------------------------------------- " ) ) ;
DBUG_PRINT ( " qcache " , ( " length used t nt " ) ) ;
DBUG_PRINT ( " qcache " , ( " ------------------------------------- " ) ) ;
Query_cache_block * i = first_block ;
do
{
DBUG_PRINT ( " qcache " ,
( " %10lu %10lu %1d %2d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx " ,
i - > length , i - > used , ( int ) i - > type ,
i - > n_tables , ( ulong ) i ,
( ulong ) i - > next , ( ulong ) i - > prev , ( ulong ) i - > pnext ,
( ulong ) i - > pprev ) ) ;
i = i - > pnext ;
} while ( i ! = first_block ) ;
DBUG_PRINT ( " qcache " , ( " ------------------------------------- " ) ) ;
}
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
void Query_cache : : queries_dump ( )
{
2002-01-02 16:55:10 +01:00
if ( ! initialized )
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
return ;
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " ------------------ " ) ) ;
DBUG_PRINT ( " qcache " , ( " QUERIES " ) ) ;
DBUG_PRINT ( " qcache " , ( " ------------------ " ) ) ;
if ( queries_blocks ! = 0 )
{
Query_cache_block * block = queries_blocks ;
do
{
uint len ;
char * str = ( char * ) query_cache_query_get_key ( ( byte * ) block , & len , 0 ) ;
2001-12-23 01:43:46 +01:00
len - - ; // Point at flags
uint flags = ( uint ) ( uchar ) str [ len ] ;
str [ len ] = 0 ;
DBUG_PRINT ( " qcache " , ( " %u (%u,%u) '%s' '%s' " ,
2001-12-05 12:03:00 +01:00
( ( flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK ) ? 1 : 0 ) ,
2001-12-23 01:43:46 +01:00
( flags & QUERY_CACHE_CHARSET_CONVERT_MASK ) , len ,
str , strend ( str ) + 1 ) ) ;
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " -b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx " , ( ulong ) block ,
( ulong ) block - > next , ( ulong ) block - > prev ,
( ulong ) block - > pnext , ( ulong ) block - > pprev ) ) ;
2001-12-23 01:43:46 +01:00
str [ len ] = ( char ) flags ;
2001-12-05 12:03:00 +01:00
for ( TABLE_COUNTER_TYPE t = 0 ; t < block - > n_tables ; t + + )
{
Query_cache_table * table = block - > table ( t ) - > parent ;
DBUG_PRINT ( " qcache " , ( " -t- '%s' '%s' " , table - > db ( ) , table - > table ( ) ) ) ;
}
Query_cache_query * header = block - > query ( ) ;
if ( header - > result ( ) )
{
Query_cache_block * result_block = header - > result ( ) ;
Query_cache_block * result_beg = result_block ;
do
{
DBUG_PRINT ( " qcache " , ( " -r- %u %lu/%lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx " ,
( uint ) result_block - > type ,
result_block - > length , result_block - > used ,
( ulong ) result_block ,
( ulong ) result_block - > next ,
( ulong ) result_block - > prev ,
( ulong ) result_block - > pnext ,
( ulong ) result_block - > pprev ) ) ;
result_block = result_block - > next ;
} while ( result_block ! = result_beg ) ;
}
} while ( ( block = block - > next ) ! = queries_blocks ) ;
}
else
{
DBUG_PRINT ( " qcache " , ( " no queries in list " ) ) ;
}
DBUG_PRINT ( " qcache " , ( " ------------------ " ) ) ;
}
2001-12-02 13:34:01 +01:00
2001-12-05 12:03:00 +01:00
void Query_cache : : tables_dump ( )
{
2002-01-02 16:55:10 +01:00
if ( ! initialized )
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
return ;
}
2001-12-05 12:03:00 +01:00
DBUG_PRINT ( " qcache " , ( " -------------------- " ) ) ;
DBUG_PRINT ( " qcache " , ( " TABLES " ) ) ;
DBUG_PRINT ( " qcache " , ( " -------------------- " ) ) ;
for ( int i = 0 ; i < ( int ) Query_cache_table : : TYPES_NUMBER ; i + + )
{
DBUG_PRINT ( " qcache " , ( " --- type %u " , i ) ) ;
if ( tables_blocks [ i ] ! = 0 )
{
Query_cache_block * table_block = tables_blocks [ i ] ;
do
{
Query_cache_table * table = table_block - > table ( ) ;
DBUG_PRINT ( " qcache " , ( " '%s' '%s' " , table - > db ( ) , table - > table ( ) ) ) ;
table_block = table_block - > next ;
} while ( table_block ! = tables_blocks [ i ] ) ;
}
else
DBUG_PRINT ( " qcache " , ( " no tables in list " ) ) ;
}
DBUG_PRINT ( " qcache " , ( " -------------------- " ) ) ;
2000-07-31 21:29:14 +02:00
}
2001-12-13 01:31:19 +01:00
2002-01-03 18:04:01 +01:00
my_bool Query_cache : : check_integrity ( bool not_locked )
2001-12-13 01:31:19 +01:00
{
my_bool result = 0 ;
uint i ;
2002-01-02 20:29:41 +01:00
DBUG_ENTER ( " check_integrity " ) ;
2001-12-13 01:31:19 +01:00
2002-01-31 03:36:58 +01:00
if ( query_cache_size = = 0 )
2002-01-02 16:55:10 +01:00
{
DBUG_PRINT ( " qcache " , ( " Query Cache not initialized " ) ) ;
2002-01-02 21:02:03 +01:00
DBUG_RETURN ( 0 ) ;
2002-01-02 16:55:10 +01:00
}
2002-01-03 18:04:01 +01:00
if ( ! not_locked )
STRUCT_LOCK ( & structure_guard_mutex ) ;
2002-01-02 16:55:10 +01:00
2001-12-13 01:31:19 +01:00
if ( hash_check ( & queries ) )
{
DBUG_PRINT ( " error " , ( " queries hash is damaged " ) ) ;
result = 1 ;
}
if ( hash_check ( & tables ) )
{
DBUG_PRINT ( " error " , ( " tables hash is damaged " ) ) ;
result = 1 ;
}
DBUG_PRINT ( " qcache " , ( " physical address check ... " ) ) ;
ulong free = 0 , used = 0 ;
Query_cache_block * block = first_block ;
do
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx, type %u... " ,
( ulong ) block , ( uint ) block - > type ) ) ;
// Check memory allocation
if ( block - > pnext = = first_block ) // Is it last block?
{
if ( ( ( byte * ) block ) + block - > length ! =
( ( byte * ) first_block ) + query_cache_size )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx " ,
( ulong ) block , ( uint ) block - > type ,
( ulong ) ( ( ( byte * ) block ) + block - > length ) ,
( ulong ) ( ( ( byte * ) first_block ) + query_cache_size ) ) ) ;
result = 1 ;
}
}
else
if ( ( ( byte * ) block ) + block - > length ! = ( ( byte * ) block - > pnext ) )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx " ,
( ulong ) block , ( uint ) block - > type ,
( ulong ) ( ( ( byte * ) block ) + block - > length ) ,
( ulong ) ( ( byte * ) block - > pnext ) ) ) ;
}
if ( block - > type = = Query_cache_block : : FREE )
free + = block - > length ;
else
used + = block - > length ;
switch ( block - > type ) {
case Query_cache_block : : FREE :
{
Query_cache_memory_bin * bin = * ( ( Query_cache_memory_bin * * )
block - > data ( ) ) ;
//is it correct pointer?
if ( ( ( byte * ) bin ) < ( ( byte * ) bins ) | |
( ( byte * ) bin ) > = ( ( byte * ) first_block ) )
{
DBUG_PRINT ( " error " ,
( " free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx] " ,
( ulong ) block ,
( ulong ) bin ,
( ulong ) bins ,
( ulong ) first_block ) ) ;
result = 1 ;
}
else
{
int idx = ( ( ( byte * ) bin ) - ( ( byte * ) bins ) ) /
sizeof ( Query_cache_memory_bin ) ;
if ( in_list ( bins [ idx ] . free_blocks , block , " free memory " ) )
result = 1 ;
}
break ;
}
case Query_cache_block : : TABLE :
if ( in_list ( tables_blocks [ block - > table ( ) - > type ( ) ] , block , " tables " ) )
result = 1 ;
2002-01-05 21:51:42 +01:00
if ( in_table_list ( block - > table ( 0 ) , block - > table ( 0 ) , " table list root " ) )
result = 1 ;
2001-12-13 01:31:19 +01:00
break ;
case Query_cache_block : : QUERY :
2002-01-29 17:32:16 +01:00
{
2001-12-13 01:31:19 +01:00
if ( in_list ( queries_blocks , block , " query " ) )
result = 1 ;
2002-01-05 21:51:42 +01:00
for ( TABLE_COUNTER_TYPE j = 0 ; j < block - > n_tables ; j + + )
{
Query_cache_block_table * block_table = block - > table ( j ) ;
Query_cache_block_table * block_table_root =
( Query_cache_block_table * )
( ( ( byte * ) block_table - > parent ) -
ALIGN_SIZE ( sizeof ( Query_cache_block_table ) ) ) ;
if ( in_table_list ( block_table , block_table_root , " table list " ) )
result = 1 ;
}
2001-12-13 01:31:19 +01:00
break ;
2002-01-29 17:32:16 +01:00
}
2001-12-13 01:31:19 +01:00
case Query_cache_block : : RES_INCOMPLETE :
2002-01-02 20:29:41 +01:00
// This type of block can be not lincked yet (in multithread environment)
break ;
2001-12-13 01:31:19 +01:00
case Query_cache_block : : RES_BEG :
case Query_cache_block : : RES_CONT :
case Query_cache_block : : RESULT :
{
Query_cache_block * query_block = block - > result ( ) - > parent ( ) ;
if ( ( ( byte * ) query_block ) < ( ( byte * ) first_block ) | |
( ( byte * ) query_block ) > = ( ( ( byte * ) first_block ) + query_cache_size ) )
{
DBUG_PRINT ( " error " ,
( " result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx] " ,
( ulong ) block ,
( ulong ) query_block ,
( ulong ) first_block ,
( ulong ) ( ( ( byte * ) first_block ) + query_cache_size ) ) ) ;
result = 1 ;
}
else
{
2002-01-02 20:29:41 +01:00
BLOCK_LOCK_RD ( query_block ) ;
2001-12-13 01:31:19 +01:00
if ( in_list ( queries_blocks , query_block , " query from results " ) )
result = 1 ;
if ( in_list ( query_block - > query ( ) - > result ( ) , block ,
" results " ) )
result = 1 ;
2002-01-02 20:29:41 +01:00
BLOCK_UNLOCK_RD ( query_block ) ;
2001-12-13 01:31:19 +01:00
}
break ;
}
default :
DBUG_PRINT ( " error " ,
( " block 0x%lx have incorrect type %u " ,
block , block - > type ) ) ;
result = 1 ;
}
block = block - > pnext ;
} while ( block ! = first_block ) ;
if ( used + free ! = query_cache_size )
{
DBUG_PRINT ( " error " ,
( " used memory (%lu) + free memory (%lu) != query_cache_size (%lu) " ,
used , free , query_cache_size ) ) ;
result = 1 ;
}
if ( free ! = free_memory )
{
DBUG_PRINT ( " error " ,
( " free memory (%lu) != free_memory (%lu) " ,
free , free_memory ) ) ;
result = 1 ;
}
DBUG_PRINT ( " qcache " , ( " check queries ... " ) ) ;
if ( ( block = queries_blocks ) )
{
do
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx, type %u... " ,
( ulong ) block , ( uint ) block - > type ) ) ;
uint length ;
byte * key = query_cache_query_get_key ( ( byte * ) block , & length , 0 ) ;
gptr val = hash_search ( & queries , key , length ) ;
if ( ( ( gptr ) block ) ! = val )
{
DBUG_PRINT ( " error " , ( " block 0x%lx found in queries hash like 0x%lx " ,
( ulong ) block , ( ulong ) val ) ) ;
}
if ( in_blocks ( block ) )
result = 1 ;
Query_cache_block * results = block - > query ( ) - > result ( ) ;
if ( results )
{
Query_cache_block * result_block = results ;
do
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx, type %u... " ,
( ulong ) block , ( uint ) block - > type ) ) ;
if ( in_blocks ( result_block ) )
result = 1 ;
result_block = result_block - > next ;
} while ( result_block ! = results ) ;
}
block = block - > next ;
} while ( block ! = queries_blocks ) ;
}
DBUG_PRINT ( " qcache " , ( " check tables ... " ) ) ;
for ( i = 0 ; ( int ) i < ( int ) Query_cache_table : : TYPES_NUMBER ; i + + )
{
if ( ( block = tables_blocks [ i ] ) )
{
do
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx, type %u... " ,
( ulong ) block , ( uint ) block - > type ) ) ;
uint length ;
byte * key = query_cache_table_get_key ( ( byte * ) block , & length , 0 ) ;
gptr val = hash_search ( & tables , key , length ) ;
if ( ( ( gptr ) block ) ! = val )
{
DBUG_PRINT ( " error " , ( " block 0x%lx found in tables hash like 0x%lx " ,
( ulong ) block , ( ulong ) val ) ) ;
}
if ( in_blocks ( block ) )
result = 1 ;
block = block - > next ;
} while ( block ! = tables_blocks [ i ] ) ;
}
}
DBUG_PRINT ( " qcache " , ( " check free blocks " ) ) ;
for ( i = 0 ; i < mem_bin_num ; i + + )
{
if ( ( block = bins [ i ] . free_blocks ) )
{
uint count = 0 ;
do
{
DBUG_PRINT ( " qcache " , ( " block 0x%lx, type %u... " ,
( ulong ) block , ( uint ) block - > type ) ) ;
if ( in_blocks ( block ) )
result = 1 ;
count + + ;
block = block - > next ;
} while ( block ! = bins [ i ] . free_blocks ) ;
if ( count ! = bins [ i ] . number )
{
2002-01-02 20:29:41 +01:00
DBUG_PRINT ( " error " , ( " bin[%d].number is %d, but bin have %d blocks " ,
bins [ i ] . number , count ) ) ;
2001-12-13 01:31:19 +01:00
result = 1 ;
}
}
}
DBUG_ASSERT ( result = = 0 ) ;
2002-01-03 18:04:01 +01:00
if ( ! not_locked )
STRUCT_UNLOCK ( & structure_guard_mutex ) ;
2002-01-02 20:29:41 +01:00
DBUG_RETURN ( result ) ;
2001-12-13 01:31:19 +01:00
}
my_bool Query_cache : : in_blocks ( Query_cache_block * point )
{
my_bool result = 0 ;
Query_cache_block * block = point ;
//back
do
{
if ( block - > pprev - > pnext ! = block )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx in physical list is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx) " ,
( ulong ) block , ( ulong ) block - > pprev ,
( ulong ) block - > pprev - > pnext ,
( ulong ) point ) ) ;
//back trace
for ( ; block ! = point ; block = block - > pnext )
DBUG_PRINT ( " error " , ( " back trace 0x%lx " , ( ulong ) block ) ) ;
result = 1 ;
goto err1 ;
}
block = block - > pprev ;
} while ( block ! = first_block & & block ! = point ) ;
if ( block ! = first_block )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx (0x%lx<-->0x%lx) not owned by pysical list " ,
( ulong ) block , ( ulong ) block - > pprev , ( ulong ) block - > pnext ) ) ;
return 1 ;
}
err1 :
//forward
block = point ;
do
{
if ( block - > pnext - > pprev ! = block )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx in physicel list is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx) " ,
( ulong ) block , ( ulong ) block - > pnext ,
( ulong ) block - > pnext - > pprev ,
( ulong ) point ) ) ;
//back trace
for ( ; block ! = point ; block = block - > pprev )
DBUG_PRINT ( " error " , ( " back trace 0x%lx " , ( ulong ) block ) ) ;
result = 1 ;
goto err2 ;
}
block = block - > pnext ;
} while ( block ! = first_block ) ;
err2 :
return result ;
}
my_bool Query_cache : : in_list ( Query_cache_block * root ,
Query_cache_block * point ,
const char * name )
{
my_bool result = 0 ;
Query_cache_block * block = point ;
//back
do
{
if ( block - > prev - > next ! = block )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx in list '%s' 0x%lx is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx) " ,
( ulong ) block , name , ( ulong ) root , ( ulong ) block - > prev ,
( ulong ) block - > prev - > next ,
( ulong ) point ) ) ;
//back trace
for ( ; block ! = point ; block = block - > next )
DBUG_PRINT ( " error " , ( " back trace 0x%lx " , ( ulong ) block ) ) ;
result = 1 ;
goto err1 ;
}
block = block - > prev ;
} while ( block ! = root & & block ! = point ) ;
if ( block ! = root )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx (0x%lx<-->0x%lx) not owned by list '%s' 0x%lx " ,
( ulong ) block ,
( ulong ) block - > prev , ( ulong ) block - > next ,
name , ( ulong ) root ) ) ;
return 1 ;
}
err1 :
// forward
block = point ;
do
{
if ( block - > next - > prev ! = block )
{
DBUG_PRINT ( " error " ,
( " block 0x%lx in list '%s' 0x%lx is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx) " ,
( ulong ) block , name , ( ulong ) root , ( ulong ) block - > next ,
( ulong ) block - > next - > prev ,
( ulong ) point ) ) ;
//back trace
for ( ; block ! = point ; block = block - > prev )
DBUG_PRINT ( " error " , ( " back trace 0x%lx " , ( ulong ) block ) ) ;
result = 1 ;
goto err2 ;
}
block = block - > next ;
} while ( block ! = root ) ;
err2 :
return result ;
}
2002-01-05 21:51:42 +01:00
void dump_node ( Query_cache_block_table * node ,
const char * call , const char * descr )
{
DBUG_PRINT ( " qcache " , ( " %s: %s: node: 0x%lx " , call , descr , ( ulong ) node ) ) ;
DBUG_PRINT ( " qcache " , ( " %s: %s: node block: 0x%lx " ,
call , descr , ( ulong ) node - > block ( ) ) ) ;
DBUG_PRINT ( " qcache " , ( " %s: %s: next: 0x%lx " , call , descr ,
( ulong ) node - > next ) ) ;
DBUG_PRINT ( " qcache " , ( " %s: %s: prev: 0x%lx " , call , descr ,
( ulong ) node - > prev ) ) ;
}
my_bool Query_cache : : in_table_list ( Query_cache_block_table * root ,
Query_cache_block_table * point ,
const char * name )
{
my_bool result = 0 ;
Query_cache_block_table * table = point ;
dump_node ( root , name , " parameter root " ) ;
//back
do
{
dump_node ( table , name , " list element << " ) ;
if ( table - > prev - > next ! = table )
{
DBUG_PRINT ( " error " ,
( " table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, prev table 0x%lx(0x%lx) refered as next to 0x%lx(0x%lx) (check from 0x%lx(0x%lx)) " ,
( ulong ) table , ( ulong ) table - > block ( ) , name ,
( ulong ) root , ( ulong ) root - > block ( ) ,
( ulong ) table - > prev , ( ulong ) table - > prev - > block ( ) ,
( ulong ) table - > prev - > next ,
( ulong ) table - > prev - > next - > block ( ) ,
( ulong ) point , ( ulong ) point - > block ( ) ) ) ;
//back trace
for ( ; table ! = point ; table = table - > next )
DBUG_PRINT ( " error " , ( " back trace 0x%lx(0x%lx) " ,
( ulong ) table , ( ulong ) table - > block ( ) ) ) ;
result = 1 ;
goto err1 ;
}
table = table - > prev ;
} while ( table ! = root & & table ! = point ) ;
if ( table ! = root )
{
DBUG_PRINT ( " error " ,
( " table 0x%lx(0x%lx) (0x%lx(0x%lx)<-->0x%lx(0x%lx)) not owned by list '%s' 0x%lx(0x%lx) " ,
( ulong ) table , ( ulong ) table - > block ( ) ,
( ulong ) table - > prev , ( ulong ) table - > prev - > block ( ) ,
( ulong ) table - > next , ( ulong ) table - > next - > block ( ) ,
name , ( ulong ) root , ( ulong ) root - > block ( ) ) ) ;
return 1 ;
}
err1 :
// forward
table = point ;
do
{
dump_node ( table , name , " list element >> " ) ;
if ( table - > next - > prev ! = table )
{
DBUG_PRINT ( " error " ,
( " table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, next table 0x%lx(0x%lx) refered as prev to 0x%lx(0x%lx) (check from 0x%lx(0x%lx)) " ,
( ulong ) table , ( ulong ) table - > block ( ) ,
name , ( ulong ) root , ( ulong ) root - > block ( ) ,
( ulong ) table - > next , ( ulong ) table - > next - > block ( ) ,
( ulong ) table - > next - > prev ,
( ulong ) table - > next - > prev - > block ( ) ,
( ulong ) point , ( ulong ) point - > block ( ) ) ) ;
//back trace
for ( ; table ! = point ; table = table - > prev )
DBUG_PRINT ( " error " , ( " back trace 0x%lx(0x%lx) " ,
( ulong ) table , ( ulong ) table - > block ( ) ) ) ;
result = 1 ;
goto err2 ;
}
table = table - > next ;
} while ( table ! = root ) ;
err2 :
return result ;
}
2001-12-05 12:03:00 +01:00
# endif /* DBUG_OFF */