summaryrefslogtreecommitdiff
path: root/sql/sql_cache.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_cache.cc')
-rw-r--r--sql/sql_cache.cc2610
1 files changed, 1415 insertions, 1195 deletions
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 38db67ba38f..bc9b95fe773 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -14,6 +14,194 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+ 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).
+*/
+
#include "mysql_priv.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -25,36 +213,36 @@
#else
#include "../myisammrg/myrg_def.h"
#endif
+#include <assert.h>
-#ifdef EXTRA_DEBUG
-#define MUTEX_LOCK(M) { DBUG_PRINT("info", ("mutex lock 0x%lx", (ulong)(M))); \
+#if defined(EXTRA_DEBUG) && !defined(DBUG_OFF)
+#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \
pthread_mutex_lock(M);}
#define SEM_LOCK(M) { int val = 0; sem_getvalue (M, &val); \
- DBUG_PRINT("info", ("sem lock 0x%lx (%d)", (ulong)(M), val)); \
- sem_wait(M); DBUG_PRINT("info", ("sem lock ok")); }
-#define MUTEX_UNLOCK(M) {DBUG_PRINT("info", ("mutex unlock 0x%lx",\
+ DBUG_PRINT("lock", ("sem lock 0x%lx (%d)", (ulong)(M), val)); \
+ sem_wait(M); DBUG_PRINT("lock", ("sem lock ok")); }
+#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
(ulong)(M))); pthread_mutex_unlock(M);}
-#define SEM_UNLOCK(M) {DBUG_PRINT("info", ("sem unlock 0x%lx", (ulong)(M))); \
- sem_post(M); DBUG_PRINT("info", ("sem unlock ok")); }
-#define STRUCT_LOCK(M) {DBUG_PRINT("info", ("%d struct lock...",__LINE__)); \
- pthread_mutex_lock(M);DBUG_PRINT("info", ("struct lock OK"));}
+#define SEM_UNLOCK(M) {DBUG_PRINT("lock", ("sem unlock 0x%lx", (ulong)(M))); \
+ sem_post(M); DBUG_PRINT("lock", ("sem unlock ok")); }
+#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
+ pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));}
#define STRUCT_UNLOCK(M) { \
- DBUG_PRINT("info", ("%d struct unlock...",__LINE__)); \
- pthread_mutex_unlock(M);DBUG_PRINT("info", ("struct unlock OK"));}
-#define BLOCK_LOCK_WR(B) {DBUG_PRINT("info", ("%d LOCK_WR 0x%lx",\
+ 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",\
__LINE__,(ulong)(B))); \
B->query()->lock_writing();}
-#define BLOCK_LOCK_RD(B) {DBUG_PRINT("info", ("%d LOCK_RD 0x%lx",\
+#define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\
__LINE__,(ulong)(B))); \
B->query()->lock_reading();}
#define BLOCK_UNLOCK_WR(B) { \
- DBUG_PRINT("info", ("%d UNLOCK_WR 0x%lx",\
+ DBUG_PRINT("lock", ("%d UNLOCK_WR 0x%lx",\
__LINE__,(ulong)(B)));B->query()->unlock_writing();}
#define BLOCK_UNLOCK_RD(B) { \
- DBUG_PRINT("info", ("%d UNLOCK_RD 0x%lx",\
+ DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\
__LINE__,(ulong)(B)));B->query()->unlock_reading();}
-#define DUMP(C) {C->bins_dump();C->cache_dump();\
- C->queries_dump();C->tables_dump();}
+#define DUMP(C) DBUG_EXECUTE("qcache", {(C)->queries_dump();(C)->tables_dump();})
#else
#define MUTEX_LOCK(M) pthread_mutex_lock(M)
#define SEM_LOCK(M) sem_wait(M)
@@ -70,24 +258,24 @@
#endif
/*****************************************************************************
- * Query_cache_block_table method(s)
- *****************************************************************************/
+ Query_cache_block_table method(s)
+*****************************************************************************/
inline Query_cache_block * Query_cache_block_table::block()
{
- return (Query_cache_block *)( ((byte*)this) -
- ALIGN_SIZE(sizeof(Query_cache_block_table))*n -
- ALIGN_SIZE(sizeof(Query_cache_block)));
+ return (Query_cache_block *)(((byte*)this) -
+ sizeof(Query_cache_block_table)*n -
+ ALIGN_SIZE(sizeof(Query_cache_block)));
};
/*****************************************************************************
- * Query_cache_block method(s)
- *****************************************************************************/
+ Query_cache_block method(s)
+*****************************************************************************/
void Query_cache_block::init(ulong block_length)
{
DBUG_ENTER("Query_cache_block::init");
- DBUG_PRINT("info", ("init block 0x%lx", (ulong) this));
+ DBUG_PRINT("qcache", ("init block 0x%lx", (ulong) this));
length = block_length;
used = 0;
type = Query_cache_block::FREE;
@@ -98,15 +286,15 @@ void Query_cache_block::init(ulong block_length)
void Query_cache_block::destroy()
{
DBUG_ENTER("Query_cache_block::destroy");
- DBUG_PRINT("info", ("destroy block 0x%lx, type %d",
- (ulong)this, type));
+ DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d",
+ (ulong) this, type));
type = INCOMPLETE;
DBUG_VOID_RETURN;
}
inline uint Query_cache_block::headers_len()
{
- return (ALIGN_SIZE(sizeof(Query_cache_block_table))*n_tables +
+ return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) +
ALIGN_SIZE(sizeof(Query_cache_block)));
}
@@ -147,7 +335,7 @@ 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)) +
- n*ALIGN_SIZE(sizeof(Query_cache_block_table))));
+ n*sizeof(Query_cache_block_table)));
}
@@ -155,8 +343,10 @@ inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
* Query_cache_table method(s)
*****************************************************************************/
-byte * Query_cache_table::cache_key(const byte *record, uint *length,
- my_bool not_used __attribute__((unused)))
+extern "C"
+{
+byte *query_cache_table_get_key(const byte *record, uint *length,
+ my_bool not_used __attribute__((unused)))
{
Query_cache_block* table_block = (Query_cache_block*) record;
*length = (table_block->used - table_block->headers_len() -
@@ -164,15 +354,11 @@ byte * Query_cache_table::cache_key(const byte *record, uint *length,
return (((byte *) table_block->data()) +
ALIGN_SIZE(sizeof(Query_cache_table)));
}
-
-void Query_cache_table::free_cache(void *entry)
-{
- //NOP
}
/*****************************************************************************
- * Query_cache_query methods
- *****************************************************************************/
+ Query_cache_query methods
+*****************************************************************************/
void Query_cache_query::init_n_lock()
{
@@ -182,17 +368,18 @@ void Query_cache_query::init_n_lock()
pthread_mutex_init(&clients_guard,MY_MUTEX_INIT_FAST);
clients = 0;
lock_writing();
- DBUG_PRINT("info", ("inited & locked query for block 0x%lx",
- ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block))));
+ DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx",
+ ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block))));
DBUG_VOID_RETURN;
}
+
void Query_cache_query::unlock_n_destroy()
{
DBUG_ENTER("Query_cache_query::unlock_n_destroy");
this->unlock_writing();
- DBUG_PRINT("info", ("destroyed & unlocked query for block 0x%lx",
- ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block))));
+ DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx",
+ ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block))));
sem_destroy(&lock);
pthread_mutex_destroy(&clients_guard);
DBUG_VOID_RETURN;
@@ -200,10 +387,10 @@ void Query_cache_query::unlock_n_destroy()
/*
- Following methods work for block rwad/write locking only in this
+ Following methods work for block read/write locking only in this
particular case and in interaction with structure_guard_mutex.
- Lock for write prevents any other locking.
+ Lock for write prevents any other locking. (exclusive use)
Lock for read prevents only locking for write.
*/
@@ -215,9 +402,9 @@ void Query_cache_query::lock_writing()
/*
Needed for finding queries, that we may delete from cache.
- We don't want wait while block become unlocked, in addition
- block locking mean that query now used and we not need to
- remove it
+ 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.
*/
my_bool Query_cache_query::try_lock_writing()
@@ -225,10 +412,10 @@ my_bool Query_cache_query::try_lock_writing()
DBUG_ENTER("Query_cache_block::try_lock_writing");
if (sem_trywait(&lock)!=0 || clients != 0)
{
- DBUG_PRINT("info", ("can't lock mutex"));
+ DBUG_PRINT("qcache", ("can't lock mutex"));
DBUG_RETURN(0);
}
- DBUG_PRINT("info", ("mutex 'lock' 0x%lx locked", (ulong) &lock));
+ DBUG_PRINT("qcache", ("mutex 'lock' 0x%lx locked", (ulong) &lock));
DBUG_RETURN(1);
}
@@ -236,8 +423,8 @@ my_bool Query_cache_query::try_lock_writing()
void Query_cache_query::lock_reading()
{
MUTEX_LOCK(&clients_guard);
- clients++;
- if (clients == 1) SEM_LOCK(&lock);
+ if (!clients++)
+ SEM_LOCK(&lock);
MUTEX_UNLOCK(&clients_guard);
}
@@ -251,32 +438,27 @@ void Query_cache_query::unlock_writing()
void Query_cache_query::unlock_reading()
{
MUTEX_LOCK(&clients_guard);
- clients--;
- if (clients == 0) SEM_UNLOCK(&lock);
+ if (--clients == 0)
+ SEM_UNLOCK(&lock);
MUTEX_UNLOCK(&clients_guard);
-
}
-
-byte * Query_cache_query::cache_key( const byte *record, uint *length,
- my_bool not_used)
+extern "C"
+{
+byte *query_cache_query_get_key(const byte *record, uint *length,
+ my_bool not_used)
{
- Query_cache_block * query_block = (Query_cache_block *) record;
+ Query_cache_block *query_block = (Query_cache_block*) record;
*length = (query_block->used - query_block->headers_len() -
ALIGN_SIZE(sizeof(Query_cache_query)));
- return (((byte *)query_block->data()) +
+ return (((byte *) query_block->data()) +
ALIGN_SIZE(sizeof(Query_cache_query)));
}
-
-
-void Query_cache_query::free_cache(void *entry)
-{
- //NOP
}
/*****************************************************************************
- * Query cache store functions
- *****************************************************************************/
+ Functions to store things into the query cache
+*****************************************************************************/
void query_cache_insert(NET *net, const char *packet, ulong length)
{
@@ -292,27 +474,29 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
if (net->query_cache_query != 0)
{
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- if (net->query_cache_query != 0)
+ Query_cache_block *query_block = ((Query_cache_block*)
+ net->query_cache_query);
+ if (query_block)
{
- Query_cache_block * query_block = (Query_cache_block*)
- net->query_cache_query;
- DUMP((&query_cache));
+ Query_cache_query *header = query_block->query();
+ Query_cache_block *result = header->result();
+
+ DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
- DBUG_PRINT("info", ("insert packet %lu bytes long",length));
- Query_cache_query * header = query_block->query();
+ DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
- Query_cache_block * result = header->result();
/*
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex); will be done by
- query_cache.append_result_data if success (if no success we need
+ 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, result))
+ if (!query_cache.append_result_data(&result, length, (gptr) packet,
+ query_block))
{
DBUG_PRINT("warning", ("Can't append data"));
header->result(result);
- DBUG_PRINT("info", ("free query 0x%lx", (ulong)query_block));
+ 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
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
@@ -327,26 +511,26 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
DBUG_VOID_RETURN;
}
+
void query_cache_abort(NET *net)
{
DBUG_ENTER("query_cache_abort");
#ifndef DBUG_OFF
- // debuging method wreck may cause this
+ // Debugging method wreck may cause this
if (query_cache.query_cache_size == 0)
DBUG_VOID_RETURN;
#endif
-
- // quick check on unlocked structure
- if (net->query_cache_query != 0)
+ if (net->query_cache_query != 0) // Quick check on unlocked structure
{
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- DUMP((&query_cache));
- Query_cache_block * query_block = ((Query_cache_block*)
+ Query_cache_block *query_block = ((Query_cache_block*)
net->query_cache_query);
- if (query_block)
+ if (query_block) // Test if changed by other thread
{
+ DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
+ // The following call will remove the lock on query_block
query_cache.free_query(query_block);
net->query_cache_query=0;
}
@@ -355,37 +539,30 @@ void query_cache_abort(NET *net)
DBUG_VOID_RETURN;
}
-void query_cache_end_of_result(NET * net)
+
+void query_cache_end_of_result(NET *net)
{
DBUG_ENTER("query_cache_end_of_result");
#ifndef DBUG_OFF
- // debuging method wreck may couse this
+ // Debugging method wreck may cause this
if (query_cache.query_cache_size == 0) DBUG_VOID_RETURN;
#endif
- //quick check on unlocked structure
- if (net->query_cache_query != 0)
+ if (net->query_cache_query != 0) // Quick check on unlocked structure
{
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- if (net->query_cache_query != 0)
+ Query_cache_block *query_block = ((Query_cache_block*)
+ net->query_cache_query);
+ if (query_block)
{
- Query_cache_block * query_block = (Query_cache_block*)
- net->query_cache_query;
- DUMP((&query_cache));
+ DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- Query_cache_query * header = query_block->query();
-#ifndef DBUG_OFF
- if (header->result() != 0)
- {
-#endif
- header->found_rows(current_thd->limit_found_rows);
- header->result()->type = Query_cache_block::RESULT;
+ Query_cache_query *header = query_block->query();
#ifndef DBUG_OFF
- }
- else
+ if (header->result() == 0)
{
DBUG_PRINT("error", ("end of data whith no result. query '%s'",
header->query()));
@@ -393,13 +570,15 @@ void query_cache_end_of_result(NET * net)
DBUG_VOID_RETURN;
}
#endif
+ header->found_rows(current_thd->limit_found_rows);
+ header->result()->type = Query_cache_block::RESULT;
net->query_cache_query=0;
header->writer(0);
BLOCK_UNLOCK_WR(query_block);
}
else
{
- //cache was flushed or resized and query was deleted => do nothing
+ // Cache was flushed or resized and query was deleted => do nothing
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
}
net->query_cache_query=0;
@@ -407,48 +586,39 @@ void query_cache_end_of_result(NET * net)
DBUG_VOID_RETURN;
}
-void query_cache_invalidate_by_MyISAM_filename(char * filename)
+void query_cache_invalidate_by_MyISAM_filename(const char *filename)
{
query_cache.invalidate_by_MyISAM_filename(filename);
}
-/*****************************************************************************
- * Query_cache methods
- *****************************************************************************/
/*****************************************************************************
- * interface methods
- *****************************************************************************/
+ Query_cache methods
+*****************************************************************************/
-Query_cache::Query_cache(
- ulong query_cache_limit,
+Query_cache::Query_cache(ulong query_cache_limit,
ulong min_allocation_unit,
ulong min_result_data_size,
uint def_query_hash_size ,
- uint def_table_hash_size):
-
- query_cache_size(0),
- query_cache_limit(query_cache_limit),
- 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),
- queries_in_cache(0), hits(0), inserts(0), refused(0),
- initialized(0)
-{
- if (min_allocation_unit < ALIGN_SIZE(sizeof(Query_cache_block)) +
- ALIGN_SIZE(sizeof(Query_cache_block_table)) +
- ALIGN_SIZE(sizeof(Query_cache_query)) + 3)
- {
- min_allocation_unit=ALIGN_SIZE(sizeof(Query_cache_block)) +
- ALIGN_SIZE(sizeof(Query_cache_block_table)) +
- ALIGN_SIZE(sizeof(Query_cache_query)) + 3;
- }
+ uint def_table_hash_size)
+ :query_cache_size(0),
+ query_cache_limit(query_cache_limit),
+ 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),
+ queries_in_cache(0), hits(0), inserts(0), refused(0),
+ 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);
this->min_allocation_unit = min_allocation_unit;
- if (min_result_data_size < min_allocation_unit)
- this->min_result_data_size = min_allocation_unit;
+ set_if_bigger(this->min_result_data_size,min_allocation_unit);
}
+
ulong Query_cache::resize(ulong query_cache_size)
{
/*
@@ -456,102 +626,93 @@ ulong Query_cache::resize(ulong query_cache_size)
query_cache_size < this->query_cache_size
*/
/*
- TODO: try to copy old cache in new mamory
+ TODO: try to copy old cache in new memory
*/
DBUG_ENTER("Query_cache::resize");
- DBUG_PRINT("info", ("from %lu to %lu",this->query_cache_size,\
- query_cache_size));
+ DBUG_PRINT("qcache", ("from %lu to %lu",this->query_cache_size,
+ query_cache_size));
free_cache(0);
this->query_cache_size=query_cache_size;
DBUG_RETURN(init_cache());
}
+
void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
{
/*
- TODO may be better convert keywords to upper case when query
- stored/compared
+ TODO:
+ Maybe better convert keywords to upper case when query stored/compared.
+ (Not important at this stage)
*/
+ TABLE_COUNTER_TYPE tables;
DBUG_ENTER("Query_cache::store_query");
if (query_cache_size == 0)
DBUG_VOID_RETURN;
- LEX * lex = &thd->lex;
- NET * net = &thd->net;
-
- TABLE_COUNTER_TYPE tables = 0;
-
- if ((tables = is_cachable(thd, thd->query_length,
- thd->query, lex, tables_used))){
+ 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);
STRUCT_LOCK(&structure_guard_mutex);
+
if (query_cache_size == 0)
DBUG_VOID_RETURN;
-
DUMP(this);
/*
- prepare flags:
- most significant bit - CLIENT_LONG_FLAG,
- other - charset number (0 no charset convertion)
+ Prepare flags:
+ most significant bit - CLIENT_LONG_FLAG,
+ other - charset number (0 no charset convertion)
*/
- byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0);
if (thd->convert_set != 0)
{
- flags |= (byte) thd->convert_set->number();
-#ifndef DBUG_OFF
- if ( (thd->convert_set->number() & QUERY_CACHE_CHARSET_CONVERT_MASK) !=
- thd->convert_set->number())
- {
- wreck(__LINE__,
- "charset number bigger than QUERY_CACHE_CHARSET_CONVERT_MASK");
- }
-#endif
+ flags|= (byte) thd->convert_set->number();
+ DBUG_ASSERT(thd->convert_set->number() < 128);
}
- /* check: Is it another thread who process same query? */
- thd->query[thd->query_length] = (char)flags;
+ /* Check if another thread is processing the same query? */
+ thd->query[thd->query_length] = (char) flags;
Query_cache_block *competitor = (Query_cache_block *)
hash_search(&queries, thd->query, thd->query_length+1);
- thd->query[thd->query_length] = '\0';
- DBUG_PRINT("info", ("competitor 0x%lx, flags %x", (ulong)competitor,
+ DBUG_PRINT("qcache", ("competitor 0x%lx, flags %x", (ulong) competitor,
flags));
-
if (competitor == 0)
{
- thd->query[thd->query_length] = (char)flags;
- Query_cache_block * query_block =
- write_block_data(thd->query_length+1,
- (gptr) thd->query,
- ALIGN_SIZE(sizeof(Query_cache_query)),
- Query_cache_block::QUERY, tables, 1);
- thd->query[thd->query_length] = '\0';
+ /* Query is not in cache and no one is working with it; Store it */
+ thd->query[thd->query_length] = (char) flags;
+ Query_cache_block *query_block;
+ query_block= write_block_data(thd->query_length+1,
+ (gptr) thd->query,
+ ALIGN_SIZE(sizeof(Query_cache_query)),
+ Query_cache_block::QUERY, tables, 1);
if (query_block != 0)
{
- DBUG_PRINT("info", ("query block 0x%lx allocated, %lu",
- (ulong)query_block, query_block->used));
+ DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
+ (ulong) query_block, query_block->used));
- Query_cache_query * header = query_block->query();
+ Query_cache_query *header = query_block->query();
header->init_n_lock();
- if (hash_insert(&queries, (byte*)query_block))
+ if (hash_insert(&queries, (byte*) query_block))
{
refused++;
- DBUG_PRINT("info", ("insertion in query hash"));
+ DBUG_PRINT("qcache", ("insertion in query hash"));
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_VOID_RETURN;
+ goto end;
}
if (!register_all_tables(query_block, tables_used, tables))
{
refused++;
- DBUG_PRINT("warning", ("tables list incliding filed"));
+ DBUG_PRINT("warning", ("tables list including failed"));
hash_delete(&queries, (char *) query_block);
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
- double_linked_list_simple_include(query_block, queries_blocks);
+ double_linked_list_simple_include(query_block, &queries_blocks);
inserts++;
queries_in_cache++;
STRUCT_UNLOCK(&structure_guard_mutex);
@@ -563,29 +724,36 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
}
else
{
- refused++;
// We have not enough memory to store query => do nothing
+ refused++;
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_PRINT("warning", ("Can't allocate query"));
}
}
else
{
+ // Another thread is processing the same query => do nothing
refused++;
- // Another thread already procass same query => do nothing
- DBUG_PRINT("info", ("Another thread process same query"));
STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_PRINT("qcache", ("Another thread process same query"));
}
}
else
refused++;
+
+end:
+ thd->query[thd->query_length]= 0; // Restore end null
DBUG_VOID_RETURN;
}
-my_bool Query_cache::send_result_to_client(
- THD *thd, char *sql, uint query_length)
-{
+my_bool
+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;
+ byte flags;
DBUG_ENTER("Query_cache::send_result_to_client");
if (query_cache_size == 0 ||
@@ -597,138 +765,142 @@ my_bool Query_cache::send_result_to_client(
thd->query_cache_type == 0)
{
- DBUG_PRINT("info", ("query cache disabled on not in autocommit mode"));
- DBUG_RETURN(1);
+ DBUG_PRINT("qcache", ("query cache disabled on not in autocommit mode"));
+ goto err;
+ }
+ /*
+ We can't cache the query if we are using a temporary table because
+ we don't know if the query is using a temporary table.
+
+ TODO: We could parse the query and then check if the query used
+ a temporary table.
+ */
+ if (thd->temporary_tables != 0 || !thd->safe_to_cache_query)
+ {
+ DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
+ goto err;
}
- char *begin = sql;
- while(*begin == ' ' || *begin == '\t') begin++;
- if ( toupper(begin[0])!='S' ||
- toupper(begin[1])!='E' ||
- toupper(begin[2])!='L')
+ /* Test if the query is a SELECT */
+ while (*sql == ' ' || *sql == '\t')
{
- DBUG_PRINT("info", ("Not look like SELECT"));
- DBUG_RETURN(1);
+ sql++;
+ query_length--;
}
- if (thd->temporary_tables != 0 || !thd->safe_to_cache_query )
+ if (toupper(sql[0]) != 'S' || toupper(sql[1]) != 'E' ||
+ toupper(sql[2]) !='L')
{
- DBUG_PRINT("info", ("SELECT is non-cachable"));
- DBUG_RETURN(1);
+ DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
+ goto err;
}
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size == 0)
{
- DBUG_PRINT("info", ("query cache disabled on not in autocommit mode"));
- DBUG_RETURN(1);
+ DBUG_PRINT("qcache", ("query cache disabled and not in autocommit mode"));
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ goto err;
}
- DBUG_PRINT("info", (" sql %u '%s'", query_length, sql));
- Query_cache_block *query_block = 0;
+ DBUG_PRINT("qcache", (" sql %u '%s'", query_length, sql));
+ Query_cache_block *query_block;
/*
prepare flags:
- most significant bit - CLIENT_LONG_FLAG,
- other - charset number (0 no charset convertion)
+ Most significant bit - CLIENT_LONG_FLAG,
+ Other - charset number (0 no charset convertion)
*/
- byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0);
+ flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0);
if (thd->convert_set != 0)
{
flags |= (byte) thd->convert_set->number();
-#ifndef DBUG_OFF
- if ( (thd->convert_set->number() & QUERY_CACHE_CHARSET_CONVERT_MASK) !=
- thd->convert_set->number())
- {
- wreck(__LINE__,
- "charset number bigger than QUERY_CACHE_CHARSET_CONVERT_MASK");
- }
-#endif
+ DBUG_ASSERT(thd->convert_set->number() < 128);
}
sql[query_length] = (char) flags;
- query_block = (Query_cache_block *)
- hash_search(&queries, sql, query_length+1);
+ query_block = (Query_cache_block *) hash_search(&queries, sql,
+ query_length+1);
sql[query_length] = '\0';
- /*quick abort on unlocked data*/
+ /* Quick abort on unlocked data */
if (query_block == 0 ||
query_block->query()->result() == 0 ||
query_block->query()->result()->type != Query_cache_block::RESULT)
{
STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_PRINT("info", ("No query in query hash or no results"));
- DBUG_RETURN(1);
+ DBUG_PRINT("qcache", ("No query in query hash or no results"));
+ goto err;
}
- DBUG_PRINT("info", ("Query in query hash 0x%lx", (ulong)query_block));
+ DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block));
- /* now lock and test that nothing changed while blocks was unlocked */
+ /* Now lock and test that nothing changed while blocks was unlocked */
BLOCK_LOCK_RD(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
- Query_cache_query * query = query_block->query();
- Query_cache_block * first_result_block = query->result();
- Query_cache_block * result_block = first_result_block;
- if (result_block == 0 ||
- result_block->type != Query_cache_block::RESULT)
- {
- DBUG_PRINT("info", ("query found, but no data or data incomplete"));
- DBUG_RETURN(1); //no data in query
- }
- DBUG_PRINT("info", ("Query have result 0x%lx", (ulong)query));
-
- //check access;
- TABLE_COUNTER_TYPE t = 0;
- for(; t < query_block->n_tables; t++)
- {
- TABLE_LIST table_list;
- table_list.next = 0;
- table_list.use_index = table_list.ignore_index = 0;
- table_list.table = 0;
- table_list.grant.grant_table = 0;
- table_list.grant.version = table_list.grant.privilege =
- table_list.grant.want_privilege = 0;
- table_list.outer_join = 0;
- table_list.straight = 0;
- table_list.updating = 0;
- table_list.shared = 0;
-
- Query_cache_table * table = query_block->table(t)->parent;
- table_list.db = table->db();
- table_list.name = table_list.real_name = table->table();
- if (check_table_access(thd,SELECT_ACL,&table_list))
- {
- DBUG_PRINT("info",
- ("probably no SELECT access to %s.%s =>\
- return to normal processing",
- table_list.db, table_list.name));
- DBUG_RETURN(1); //no access
- }
+ 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"));
+ goto err;
}
+ 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));
+ Query_cache_table *table = block_table->parent;
+ table_list.db = table->db();
+ table_list.name = table_list.real_name = table->table();
+ if (check_table_access(thd,SELECT_ACL,&table_list))
+ {
+ DBUG_PRINT("qcache",
+ ("probably no SELECT access to %s.%s => return to normal processing",
+ table_list.db, table_list.name));
+ goto err;
+ }
+ }
move_to_query_list_end(query_block);
hits++;
+ /*
+ Send cached result to client
+ */
do
{
- DBUG_PRINT("info", ("Results (len %lu, used %lu, headers %lu)",
+ DBUG_PRINT("qcache", ("Results (len %lu, used %lu, headers %lu)",
result_block->length, result_block->used,
result_block->headers_len()+
ALIGN_SIZE(sizeof(Query_cache_result))));
- Query_cache_result * result = result_block->result();
- net_real_write(&thd->net, result->data(),
- result_block->used -
- result_block->headers_len() -
- ALIGN_SIZE(sizeof(Query_cache_result)));
+ 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
result_block = result_block->next;
} while (result_block != first_result_block);
thd->limit_found_rows = query->found_rows();
BLOCK_UNLOCK_RD(query_block);
-
DBUG_RETURN(0);
+
+err:
+ DBUG_RETURN(1);
}
+/*
+ Remove all cached queries that uses any of the tables in the list
+*/
+
void Query_cache::invalidate(TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
@@ -746,6 +918,10 @@ void Query_cache::invalidate(TABLE_LIST *tables_used)
DBUG_VOID_RETURN;
}
+/*
+ Remove all cached queries that uses the given table
+*/
+
void Query_cache::invalidate(TABLE *table)
{
DBUG_ENTER("Query_cache::invalidate (table)");
@@ -759,6 +935,14 @@ void Query_cache::invalidate(TABLE *table)
DBUG_VOID_RETURN;
}
+
+/*
+ Remove all cached queries that uses the given table type.
+ TODO: This is currently used to invalidate InnoDB tables on commit.
+ We should remove this function and only invalidate tables
+ used in the transaction.
+*/
+
void Query_cache::invalidate(Query_cache_table::query_cache_table_type type)
{
DBUG_ENTER("Query_cache::invalidate (type)");
@@ -771,25 +955,27 @@ void Query_cache::invalidate(Query_cache_table::query_cache_table_type type)
Query_cache_block *table_block = tables_blocks[type];
do
{
- /*
- store next block address defore deletetion of current block
- */
+ /* Store next block address defore deleting the current block */
Query_cache_block *next = table_block->next;
invalidate_table(table_block);
- if (next == table_block)
+ if (next == table_block) // End of list
break;
table_block = next;
- } while (table_block != tables_blocks[type]);
-
+ } while (table_block != tables_blocks[type]);
}
STRUCT_UNLOCK(&structure_guard_mutex);
}
DBUG_VOID_RETURN;
}
+
+/*
+ Remove all cached queries that uses the given database
+*/
+
void Query_cache::invalidate(char *db)
{
DBUG_ENTER("Query_cache::invalidate (db)");
@@ -802,13 +988,13 @@ void Query_cache::invalidate(char *db)
int i = 0;
for(; i < (int) Query_cache_table::TYPES_NUMBER; i++)
{
- if (tables_blocks[i] != 0) //cache not empty
+ if (tables_blocks[i] != 0) // Cache not empty
{
Query_cache_block *table_block = tables_blocks[i];
do
{
/*
- store next block address defore deletetion of current block
+ Store next block address defore deletetion of current block
*/
Query_cache_block *next = table_block->next;
@@ -827,27 +1013,29 @@ void Query_cache::invalidate(char *db)
DBUG_VOID_RETURN;
}
-void Query_cache::invalidate_by_MyISAM_filename(char * filename)
+
+void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
if (query_cache_size > 0)
{
+ /* 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);
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0)
+ if (query_cache_size > 0) // Safety if cache removed
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length =
- Query_cache::filename_2_table_key(key, filename);
-
- Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*)
- hash_search(&tables, key, key_length)))
- invalidate_table(table_block);
+ Query_cache_block *table_block;
+ if ((table_block = (Query_cache_block*) hash_search(&tables, key,
+ key_length)))
+ invalidate_table(table_block);
}
STRUCT_UNLOCK(&structure_guard_mutex);
}
DBUG_VOID_RETURN;
}
+
+
void Query_cache::flush()
{
DBUG_ENTER("Query_cache::flush");
@@ -873,6 +1061,7 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit)
DBUG_VOID_RETURN;
}
+
void Query_cache::destroy()
{
DBUG_ENTER("Query_cache::destroy");
@@ -882,161 +1071,10 @@ void Query_cache::destroy()
DBUG_VOID_RETURN;
}
-#ifndef DBUG_OFF
-
-void Query_cache::wreck(uint line, const char * message)
-{
- DBUG_ENTER("Query_cache::wreck");
- query_cache_size = 0;
- DBUG_PRINT("error", (" %s", message));
- DBUG_PRINT("warning", ("=================================="));
- DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line));
- DBUG_PRINT("warning", ("=================================="));
- current_thd->killed = 1;
- bins_dump();
- cache_dump();
- DBUG_VOID_RETURN;
-}
-
-void Query_cache::bins_dump()
-{
- DBUG_PRINT("info", ("mem_bin_num=%u, mem_bin_steps=%u",
- mem_bin_num, mem_bin_steps));
- DBUG_PRINT("info", ("-------------------------"));
- DBUG_PRINT("info", (" size idx step"));
- DBUG_PRINT("info", ("-------------------------"));
- uint i = 0;
- for(; i < mem_bin_steps; i++)
- {
- DBUG_PRINT("info", ("%10lu %3d %10lu", steps[i].size, steps[i].idx,
- steps[i].increment));
- }
- DBUG_PRINT("info", ("-------------------------"));
- DBUG_PRINT("info", (" size num"));
- DBUG_PRINT("info", ("-------------------------"));
- i = 0;
- for(; i < mem_bin_num; i++)
- {
- DBUG_PRINT("info", ("%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("info", ("\\-- %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("info", ("-------------------------"));
-}
-
-void Query_cache::cache_dump()
-{
- DBUG_PRINT("info", ("-------------------------------------"));
- DBUG_PRINT("info", (" length used t nt"));
- DBUG_PRINT("info", ("-------------------------------------"));
- Query_cache_block * i = first_block;
- do
- {
- DBUG_PRINT("info",
- ("%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("info", ("-------------------------------------"));
-}
-void Query_cache::queries_dump()
-{
- DBUG_PRINT("info", ("------------------"));
- DBUG_PRINT("info", (" QUERIES"));
- DBUG_PRINT("info", ("------------------"));
- if (queries_blocks != 0)
- {
- Query_cache_block * i = queries_blocks;
- do
- {
- uint len;
- char * str = (char*) Query_cache_query::cache_key((byte*) i, &len, 0);
- byte flags = (byte) str[len-1];
- str[len-1] = 0; //safe only under structure_guard_mutex locked
- DBUG_PRINT("info", ("%u (%u,%u) %s",len,
- ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)?1:0),
- (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), str));
- str[len-1] = (char)flags;
- DBUG_PRINT("info", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong)i,
- (ulong)i->next, (ulong)i->prev, (ulong)i->pnext,
- (ulong)i->pprev));
- TABLE_COUNTER_TYPE t = 0;
- for (; t < i->n_tables; t++)
- {
- Query_cache_table * table = i->table(t)->parent;
- DBUG_PRINT("info", ("-t- '%s' '%s'", table->db(), table->table()));
- }
- Query_cache_query * header = i->query();
- if (header->result())
- {
- Query_cache_block * result_block = header->result();
- Query_cache_block * result_beg = result_block;
- do
- {
- DBUG_PRINT("info", ("-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 );
- }
- i = i->next;
- } while ( i != queries_blocks );
- }
- else
- {
- DBUG_PRINT("info", ("no queries in list"));
- }
- DBUG_PRINT("info", ("------------------"));
-}
-
-void Query_cache::tables_dump()
-{
- DBUG_PRINT("info", ("--------------------"));
- DBUG_PRINT("info", ("TABLES"));
- DBUG_PRINT("info", ("--------------------"));
- int i = 0;
- for(; i < (int) Query_cache_table::TYPES_NUMBER; i++)
- {
- DBUG_PRINT("info", ("--- 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("info", ("'%s' '%s'", table->db(), table->table()));
- table_block = table_block->next;
- } while ( table_block != tables_blocks[i]);
- }
- else
- DBUG_PRINT("info", ("no tables in list"));
- }
- DBUG_PRINT("info", ("--------------------"));
-}
-
-#endif
/*****************************************************************************
- * init/destroy
- *****************************************************************************/
+ init/destroy
+*****************************************************************************/
void Query_cache::init()
{
@@ -1046,205 +1084,174 @@ void Query_cache::init()
DBUG_VOID_RETURN;
}
+
ulong Query_cache::init_cache()
{
+ 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;
+
DBUG_ENTER("Query_cache::init_cache");
if (!initialized)
- {
- DBUG_PRINT("info", ("first time init"));
init();
- }
- ulong additional_data_size = 0,
- approx_additional_data_size = sizeof(Query_cache) +
- sizeof(gptr)*(def_query_hash_size+def_query_hash_size);
-
- ulong max_mem_bin_size = 0;
+ approx_additional_data_size = (sizeof(Query_cache) +
+ sizeof(gptr)*(def_query_hash_size+
+ def_query_hash_size));
if (query_cache_size < approx_additional_data_size)
- {
- additional_data_size = 0;
- approx_additional_data_size = 0;
- make_disabled();
- }
- else
- {
- query_cache_size -= approx_additional_data_size;
+ goto err;
- //Count memory bins number.
+ query_cache_size -= approx_additional_data_size;
- /*
- The idea is inherited from GNU malloc with some add-ons.
- Free memory blocks are stored in bins according to their sizes.
- The bins are stored in size-descending order.
- The bins are approximately logarithmically separated by size.
-
- Opposite to GNU malloc bin splitting is not fixed but calculated
- depending on cache size. Spliting calculating stored in cache and
- then used for bin finding.
-
- 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):
+ /*
+ Count memory bins number.
+ Check section 6. in start comment for the used algorithm.
+ */
- | 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
- */
- max_mem_bin_size =
- query_cache_size >> QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2;
- uint mem_bin_count = 1 + QUERY_CACHE_MEM_BIN_PARTS_INC;
- mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL);
- mem_bin_num = 1;
- mem_bin_steps = 1;
- ulong mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2;
- ulong 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;
- mem_bin_steps++;
- 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);
- }
- ulong inc = (prev_size - mem_bin_size) / mem_bin_count;
- mem_bin_num += (mem_bin_count - (min_allocation_unit - mem_bin_size)/inc);
+ 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;
mem_bin_steps++;
- additional_data_size = mem_bin_num *
- ALIGN_SIZE(sizeof(Query_cache_memory_bin))+
- mem_bin_steps * ALIGN_SIZE(sizeof(Query_cache_memory_bin_step));
+ 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);
}
+ 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))));
+
if (query_cache_size < additional_data_size)
- {
- additional_data_size = 0;
- approx_additional_data_size = 0;
- make_disabled();
- }
- else
- query_cache_size -= additional_data_size;
+ goto err;
+ query_cache_size -= additional_data_size;
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size <= min_allocation_unit)
{
- DBUG_PRINT("info",
+ DBUG_PRINT("qcache",
(" query_cache_size <= min_allocation_unit => cache disabled"));
- make_disabled();
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ goto err;
}
- else
+
+ if (!(cache = (byte *)
+ my_malloc_lock(query_cache_size+additional_data_size, MYF(0))))
{
- if ( (cache = (byte *)
- my_malloc_lock(query_cache_size+additional_data_size, MYF(0)))==0 )
- {
- DBUG_PRINT("warning",
- ("can't allocate query cache memory => cache disabled"));
- make_disabled();
- }
- else
- {
- DBUG_PRINT("info",
- ("cache length %lu, min unit %lu, %u bins",
- query_cache_size, min_allocation_unit, mem_bin_num));
-
- steps = (Query_cache_memory_bin_step *) cache;
- bins = (Query_cache_memory_bin *)
- (cache +
- mem_bin_steps * ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)));
-
- first_block = (Query_cache_block *) (cache + additional_data_size);
- first_block->init(query_cache_size);
- first_block->pnext=first_block->pprev=first_block;
- first_block->next=first_block->prev=first_block;
-
- free_memory = query_cache_size;
-
- /* prepare bins */
-
- bins[0].init(max_mem_bin_size);
- steps[0].init(max_mem_bin_size,0,0);
- uint mem_bin_count = 1 + QUERY_CACHE_MEM_BIN_PARTS_INC;
- mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL);
- uint num = 1;
- ulong mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2;
- uint step = 1;
- while (mem_bin_size > min_allocation_unit)
- {
- ulong inc = (steps[step-1].size - mem_bin_size) / mem_bin_count;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ goto err;
+ }
- unsigned long size = mem_bin_size;
- uint i = mem_bin_count;
- for(; i > 0; i--)
- {
- bins[num+i-1].init(size);
- size += inc;
- }
- num += mem_bin_count;
- steps[step].init(mem_bin_size, num-1, inc);
- 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);
- }
- ulong 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;
- for(; i > 0; i--)
- {
- bins[num+i-1].init(size);
- size += inc;
- }
+ DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins",
+ query_cache_size, min_allocation_unit, mem_bin_num));
- insert_into_free_memory_list(first_block);
+ steps = (Query_cache_memory_bin_step *) cache;
+ bins = ((Query_cache_memory_bin *)
+ (cache + mem_bin_steps *
+ ALIGN_SIZE(sizeof(Query_cache_memory_bin_step))));
- DUMP(this);
+ first_block = (Query_cache_block *) (cache + additional_data_size);
+ first_block->init(query_cache_size);
+ first_block->pnext=first_block->pprev=first_block;
+ first_block->next=first_block->prev=first_block;
- VOID(hash_init(&queries,def_query_hash_size, 0, 0,
- Query_cache_query::cache_key,
- (void (*)(void*))Query_cache_query::free_cache,
- 0));
- VOID(hash_init(&tables,def_table_hash_size, 0, 0,
- Query_cache_table::cache_key,
- (void (*)(void*))Query_cache_table::free_cache,
- 0));
+ /* 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;
}
+ 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);
}
+ 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;
+ }
+ }
+ bins[mem_bin_num].number= 1; // For easy end test
+ free_memory= 0;
+ 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));
+
queries_in_cache = 0;
queries_blocks = 0;
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(query_cache_size +
additional_data_size + approx_additional_data_size);
+
+err:
+ make_disabled();
+ DBUG_RETURN(0);
}
+
+/* Disable the use of the query cache */
+
void Query_cache::make_disabled()
{
DBUG_ENTER("Query_cache::make_disabled");
- query_cache_size = 0;
- free_memory = 0;
- bins = 0;
- steps = 0;
- cache = 0;
- mem_bin_num = mem_bin_steps = 0;
+ 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;
DBUG_VOID_RETURN;
}
+
void Query_cache::free_cache(my_bool destruction)
{
DBUG_ENTER("Query_cache::free_cache");
@@ -1261,20 +1268,14 @@ void Query_cache::free_cache(my_bool destruction)
}
#endif
- bins[0].free_blocks->destroy(); /* all cache memory must be
- in one this block */
- DBUG_PRINT("info", ("free memory %lu (should be %lu)",
- free_memory , query_cache_size));
- free_memory = 0;
-
- my_free((gptr)cache, MYF(MY_ALLOW_ZERO_PTR));
- first_block = 0;
- bins = 0;
- cache = 0;
- query_cache_size = 0;
+ /* Becasue we did a flush, all cache memory must be in one this block */
+ bins[0].free_blocks->destroy();
+ DBUG_PRINT("qcache", ("free memory %lu (should be %lu)",
+ free_memory , query_cache_size));
+ my_free((gptr) cache, MYF(MY_ALLOW_ZERO_PTR));
+ make_disabled();
hash_free(&queries);
hash_free(&tables);
- queries_in_cache = 0;
if (!destruction)
STRUCT_UNLOCK(&structure_guard_mutex);
}
@@ -1282,85 +1283,100 @@ void Query_cache::free_cache(my_bool destruction)
}
/*****************************************************************************
- * free block data
- *****************************************************************************/
+ Free block data
+*****************************************************************************/
+
+/*
+ The following assumes we have a lock on the cache
+*/
void Query_cache::flush_cache()
{
- while(queries_blocks != 0){
+ while (queries_blocks != 0)
+ {
BLOCK_LOCK_WR(queries_blocks);
free_query(queries_blocks);
}
}
+/*
+ Free oldest query that is not in use by another thread.
+ Returns 1 if we couldn't remove anything
+*/
+
my_bool Query_cache::free_old_query()
{
DBUG_ENTER("Query_cache::free_old_query");
- if (queries_blocks == 0)
+ if (!queries_blocks)
{
- DBUG_RETURN(0);
- }
- /*
- try_lock_writing used to prevent clinch 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)
- {
- Query_cache_block *i = queries_blocks;
- do
+ /*
+ 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)
{
- Query_cache_query * header = i->query();
- if (header->result() != 0 &&
- header->result()->type == Query_cache_block::RESULT &&
- i->query()->try_lock_writing())
+ Query_cache_block *block = queries_blocks;
+ /* Search until we find first query that we can remove */
+ do
{
- query_block = i;
- break;
- }
- i = i->next;
- } while ( i != queries_blocks );
- }
+ 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 );
+ }
- if (query_block != 0)
- {
- free_query(query_block);
- DBUG_RETURN(1);
+ if (query_block != 0)
+ {
+ free_query(query_block);
+ DBUG_RETURN(0);
+ }
}
- else
- DBUG_RETURN(0);
+ DBUG_RETURN(1); // Nothing to remove
}
-/* query_block must be lock_writing() */
-void Query_cache::free_query(Query_cache_block * query_block)
+/*
+ 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)
{
DBUG_ENTER("Query_cache::free_query");
- DBUG_PRINT("info", ("free query 0x%lx %lu bytes result",
- (ulong)query_block,
+ DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
+ (ulong) query_block,
query_block->query()->length() ));
+
queries_in_cache--;
hash_delete(&queries,(byte *) query_block);
- Query_cache_query * query = query_block->query();
+ Query_cache_query *query = query_block->query();
+
if (query->writer() != 0)
{
+ /* Tell MySQL that this query should not be cached anymore */
query->writer()->query_cache_query = 0;
query->writer(0);
}
- double_linked_list_exclude(query_block, queries_blocks);
- TABLE_COUNTER_TYPE i = 0;
- for(; i < query_block->n_tables; i++)
- {
- unlink_table(query_block->table(i));
- }
+ 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++);
Query_cache_block *result_block = query->result();
if (result_block != 0)
{
- Query_cache_block * block = result_block;
+ Query_cache_block *block = result_block;
do
{
- Query_cache_block * current = block;
+ Query_cache_block *current = block;
block = block->next;
free_memory_block(current);
} while (block != result_block);
@@ -1373,8 +1389,8 @@ void Query_cache::free_query(Query_cache_block * query_block)
}
/*****************************************************************************
- * query data creation
- *****************************************************************************/
+ Query data creation
+*****************************************************************************/
Query_cache_block *
Query_cache::write_block_data(ulong data_len, gptr data,
@@ -1383,217 +1399,223 @@ Query_cache::write_block_data(ulong data_len, gptr data,
TABLE_COUNTER_TYPE ntab,
my_bool under_guard)
{
+ 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;
DBUG_ENTER("Query_cache::write_block_data");
- ulong all_headers_len = ALIGN_SIZE(sizeof(Query_cache_block)) +
- ntab*ALIGN_SIZE(sizeof(Query_cache_block_table)) +
- header_len;
- DBUG_PRINT("info", ("data: %ld, header: %ld, all header: %ld",
+ DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
data_len, header_len, all_headers_len));
- ulong len = data_len + all_headers_len;
- Query_cache_block * block = allocate_block(max(len, min_allocation_unit),
- 1, 0, under_guard);
+ Query_cache_block *block = allocate_block(max(len, min_allocation_unit),
+ 1, 0, under_guard);
if (block != 0)
{
block->type = type;
block->n_tables = ntab;
block->used = len;
- memcpy((void*)(((byte *) block)+
- all_headers_len),
- (void*)data,
- data_len);
+ memcpy((void*) (((byte *) block)+ all_headers_len),
+ (void*) data, data_len);
}
DBUG_RETURN(block);
}
+
+/*
+ On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be done.
+*/
+
my_bool
-Query_cache::append_result_data(Query_cache_block * &result,
+Query_cache::append_result_data(Query_cache_block **current_block,
ulong data_len, gptr data,
- Query_cache_block * query_block,
- Query_cache_block * first_data_block)
+ Query_cache_block *query_block)
{
- DBUG_ENTER("Query_cache::uppend_result_data");
- DBUG_PRINT("info", ("append %lu bytes to 0x%lx query"));
+ DBUG_ENTER("Query_cache::append_result_data");
+ DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query",
+ data_len, query_block));
+
if (query_block->query()->add(data_len) > query_cache_limit)
{
- DBUG_PRINT("info", ("size limit reached %lu > %lu",
+ DBUG_PRINT("qcache", ("size limit reached %lu > %lu",
query_block->query()->length(),
query_cache_limit));
- result=0;
+ *current_block=0; // Mark error
DBUG_RETURN(0);
}
- if (first_data_block == 0)
+ if (*current_block == 0)
{
- DBUG_PRINT("info", ("allocated first result data block 0x%xl", data_len));
+ DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len));
/*
- STRUCT_UNLOCK(&structure_guard_mutex); will be done by
- query_cache.append_result_data if success;
+ STRUCT_UNLOCK(&structure_guard_mutex) Will be done by
+ write_result_data if success;
*/
- DBUG_RETURN(write_result_data(result, data_len, data, query_block,
+ DBUG_RETURN(write_result_data(current_block, data_len, data, query_block,
Query_cache_block::RES_BEG));
}
- else
- {
- result = first_data_block;
- }
- Query_cache_block * last_block = first_data_block->prev;
+ Query_cache_block *last_block = (*current_block)->prev;
- DBUG_PRINT("info", ("lastblock 0x%lx len %lu used %lu",
- (ulong)last_block, last_block->length,
+ DBUG_PRINT("qcache", ("lastblock 0x%lx len %lu used %lu",
+ (ulong) last_block, last_block->length,
last_block->used));
my_bool success = 1;
+ ulong last_block_free_space= last_block->length - last_block->used;
- ulong last_block_free_space = last_block->length - last_block->used;
-
- //write 'tail' of data, that can't be appended to last block
+ /*
+ 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.
+ */
- //try join blocks if physicaly next block is free...
+ // Try join blocks if physically next block is free...
if (last_block_free_space < data_len &&
append_next_free_block(last_block,
max(data_len - last_block_free_space,
QUERY_CACHE_MIN_RESULT_DATA_SIZE)))
last_block_free_space = last_block->length - last_block->used;
- //if no space in last block (even after join) allocate new block
+ // If no space in last block (even after join) allocate new block
if (last_block_free_space < data_len)
{
- //TODO try get memory from next free block (if exist) (is it needed?)
- DBUG_PRINT("info", ("allocate new block for %lu bytes",
+ // TODO: Try get memory from next free block (if exist) (is it needed?)
+ DBUG_PRINT("qcache", ("allocate new block for %lu bytes",
data_len-last_block_free_space));
Query_cache_block *new_block = 0;
/*
- STRUCT_UNLOCK(&structure_guard_mutex); will be done by
- query_cache.append_result_data
+ On success STRUCT_UNLOCK(&structure_guard_mutex) will be done
+ by the next call
*/
- success = write_result_data(new_block, data_len-last_block_free_space,
+ success = write_result_data(&new_block, data_len-last_block_free_space,
(gptr)(((byte*)data)+last_block_free_space),
query_block,
Query_cache_block::RES_CONT);
/*
new_block may be not 0 even !success (if write_result_data
- allocate small block but filed allocate continue
+ allocate small block but failed allocate continue
*/
if (new_block != 0)
double_linked_list_join(last_block, new_block);
}
else
- //it is success (nobody can prevent us write data)
+ {
+ // It is success (nobody can prevent us write data)
STRUCT_UNLOCK(&structure_guard_mutex);
+ }
- // append last block (if it is possible)
- if (last_block_free_space > 0)
+ // Now finally write data to the last block
+ if (success && last_block_free_space > 0)
{
ulong to_copy = min(data_len,last_block_free_space);
- DBUG_PRINT("info", ("use free space %lub at block 0x%lx to copy %lub",
+ DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub",
last_block_free_space, (ulong)last_block, to_copy));
- memcpy((void*)(((byte*)last_block)+last_block->used),
- (void*)data,to_copy);
+ memcpy((void*) (((byte*) last_block) + last_block->used), (void*) data,
+ to_copy);
last_block->used+=to_copy;
}
-
DBUG_RETURN(success);
}
-my_bool Query_cache::write_result_data(Query_cache_block * &result_block,
+
+my_bool Query_cache::write_result_data(Query_cache_block **result_block,
ulong data_len, gptr data,
- Query_cache_block * query_block,
+ Query_cache_block *query_block,
Query_cache_block::block_type type)
{
DBUG_ENTER("Query_cache::write_result_data");
- DBUG_PRINT("info", ("data_len %lu",data_len));
+ DBUG_PRINT("qcache", ("data_len %lu",data_len));
- //reserve block(s) for filling
+ // Reserve block(s) for filling
my_bool success = allocate_data_chain(result_block, data_len, query_block);
if (success)
{
- //it is success (nobody can prevent us write data)
+ // It is success (nobody can prevent us write data)
STRUCT_UNLOCK(&structure_guard_mutex);
- 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
+ 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
do
{
block->type = type;
ulong length = block->used - headers_len;
- DBUG_PRINT("info", ("write %lu byte in block 0x%lx",length,
+ DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length,
(ulong)block));
- memcpy((void*)(((byte*)block)+headers_len),
- (void*)rest,length);
-
+ memcpy((void*)(((byte*) block)+headers_len), (void*) rest, length);
rest += length;
block = block->next;
type = Query_cache_block::RES_CONT;
- } while (block != result_block);
+ } while (block != *result_block);
}
else
{
- if (result_block != 0)
+ if (*result_block != 0)
{
- // destroy list of blocks that created & locked by lock_result_data
- Query_cache_block * block = result_block;
+ // Destroy list of blocks that was created & locked by lock_result_data
+ Query_cache_block *block = *result_block;
do
{
- Query_cache_block * current = block;
+ Query_cache_block *current = block;
block = block->next;
free_memory_block(current);
- } while (block != result_block);
- result_block = 0;
+ } while (block != *result_block);
+ *result_block = 0;
/*
- it is not success => not unlock structure_guard_mutex (we need it to
- free query)
+ It is not success => not unlock structure_guard_mutex (we need it to
+ free query)
*/
}
}
- DBUG_PRINT("info", ("success %d", (int) success));
+ DBUG_PRINT("qcache", ("success %d", (int) success));
DBUG_RETURN(success);
}
-my_bool Query_cache::allocate_data_chain(Query_cache_block * &result_block,
+/*
+ Allocate one or more blocks to hold data
+*/
+
+my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
ulong data_len,
- Query_cache_block * query_block)
+ Query_cache_block *query_block)
{
- DBUG_ENTER("Query_cache::allocate_data_chain");
-
- ulong all_headers_len = ALIGN_SIZE(sizeof(Query_cache_block)) +
- ALIGN_SIZE(sizeof(Query_cache_result));
+ ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
+ ALIGN_SIZE(sizeof(Query_cache_result)));
ulong len = data_len + all_headers_len;
- DBUG_PRINT("info", ("data_len %lu, all_headers_len %lu",
+ DBUG_ENTER("Query_cache::allocate_data_chain");
+ DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu",
data_len, all_headers_len));
- result_block = allocate_block(max(min_result_data_size,len),
- min_result_data_size == 0,
- all_headers_len + min_result_data_size,
- 1);
- my_bool success = (result_block != 0);
+ *result_block = allocate_block(max(min_result_data_size,len),
+ min_result_data_size == 0,
+ all_headers_len + min_result_data_size,
+ 1);
+ my_bool success = (*result_block != 0);
if (success)
{
- result_block->n_tables = 0;
- result_block->used = 0;
- result_block->type = Query_cache_block::RES_INCOMPLETE;
- result_block->next = result_block->prev = result_block;
- Query_cache_result * header = result_block->result();
+ 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();
header->parent(query_block);
- Query_cache_block * next_block = 0;
- if (result_block->length < len)
+ if (new_block->length < len)
{
/*
- allocated less memory then we need (no big memory blocks) =>
- to be continue
+ We got less memory then we need (no big memory blocks) =>
+ Continue to allocated more blocks until we got everything we need.
*/
- Query_cache_block * next_block;
- if ((success = allocate_data_chain(next_block,
- len - result_block->length,
+ Query_cache_block *next_block;
+ if ((success = allocate_data_chain(&next_block,
+ len - new_block->length,
query_block)))
- double_linked_list_join(result_block, next_block);
+ double_linked_list_join(new_block, next_block);
}
if (success)
{
- result_block->used = min(len, result_block->length);
+ new_block->used = min(len, new_block->length);
- DBUG_PRINT("info", ("Block len %lu used %lu",
- result_block->length, result_block->used));
+ DBUG_PRINT("qcache", ("Block len %lu used %lu",
+ new_block->length, new_block->used));
}
else
DBUG_PRINT("warning", ("Can't allocate block for continue"));
@@ -1604,25 +1626,28 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block * &result_block,
}
/*****************************************************************************
- * tables management
- *****************************************************************************/
+ Tables management
+*****************************************************************************/
+
+/*
+ Invalidate the first table in the table_list
+*/
void Query_cache::invalidate_table(TABLE_LIST *table_list)
{
if (table_list->table != 0)
- invalidate_table(table_list->table);
+ invalidate_table(table_list->table); // Table is open
else
{
- char key[MAX_DBKEY_LENGTH], *key_ptr;
+ char key[MAX_DBKEY_LENGTH];
uint key_length;
Query_cache_block *table_block;
key_length=(uint) (strmov(strmov(key,table_list->db)+1,
table_list->real_name) -key)+ 1;
- key_ptr = key;
- // we dont store temporary tables => no key_length+=4 ...
+ // We don't store temporary tables => no key_length+=4 ...
if ((table_block = (Query_cache_block*)
- hash_search(&tables,key_ptr,key_length)))
+ hash_search(&tables,key,key_length)))
invalidate_table(table_block);
}
}
@@ -1630,302 +1655,328 @@ void Query_cache::invalidate_table(TABLE_LIST *table_list)
void Query_cache::invalidate_table(TABLE *table)
{
Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*)
- hash_search(&tables,table->table_cache_key,table->key_length)))
+ if ((table_block = ((Query_cache_block*)
+ hash_search(&tables, table->table_cache_key,
+ table->key_length))))
invalidate_table(table_block);
}
void Query_cache::invalidate_table_in_db(Query_cache_block *table_block,
- char * db)
+ char *db)
{
/*
table key consist of data_base_name + '\0' + table_name +'\0'...
- => we may use strcmp.
+ => we may use strcmp to compare database names.
*/
- if (strcmp(db, (char*)(table_block->table()->db()))==0)
- {
+ if (strcmp(db, (char*)(table_block->table()->db())) == 0)
invalidate_table(table_block);
- }
}
+
void Query_cache::invalidate_table(Query_cache_block *table_block)
{
- Query_cache_block_table * list_root = table_block->table(0);
- while(list_root->next != list_root)
+ Query_cache_block_table *list_root = table_block->table(0);
+ while (list_root->next != list_root)
{
- Query_cache_block * query_block = list_root->next->block();
+ Query_cache_block *query_block = list_root->next->block();
BLOCK_LOCK_WR(query_block);
free_query(query_block);
}
}
-my_bool Query_cache::register_all_tables(Query_cache_block * block,
- TABLE_LIST * tables_used,
+
+my_bool Query_cache::register_all_tables(Query_cache_block *block,
+ TABLE_LIST *tables_used,
TABLE_COUNTER_TYPE tables)
{
- DBUG_PRINT("info", ("register tables block 0x%lx, n %d, header %x",
- (ulong)block, (int) tables,
- (int)ALIGN_SIZE(sizeof(Query_cache_block)) ));
+ 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))));
- TABLE_COUNTER_TYPE n = 0;
- TABLE_LIST * i = tables_used;
- for(; i != 0; i=i->next, n++)
+ Query_cache_block_table *block_table = block->table(0);
+
+ for (n=0; tables_used; tables_used=tables_used->next, n++, block_table++)
{
- DBUG_PRINT("info",
+ DBUG_PRINT("qcache",
("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx",
- i->real_name, i->db, (ulong) i->table,
- i->table->key_length,
- (ulong) i->table->table_cache_key));
- Query_cache_block_table * block_table = block->table(n);
+ tables_used->real_name, tables_used->db,
+ (ulong) tables_used->table,
+ tables_used->table->key_length,
+ (ulong) tables_used->table->table_cache_key));
block_table->n=n;
- if (!insert_table(i->table->key_length,
- i->table->table_cache_key, block_table,
- Query_cache_table::type_convertion(i->table->db_type)))
+ 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)))
break;
- if (i->table->db_type == DB_TYPE_MRG_MYISAM)
+ /*
+ TODO: (Low priority)
+ The following has to be recoded to not test for a specific table
+ type but instead call a handler function that does this for us.
+ Something like the following:
+
+ tables_used->table->file->register_used_filenames(callback,
+ first_argument);
+ */
+ if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM)
{
- ha_myisammrg * handler = (ha_myisammrg *)i->table->file;
+ ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file;
MYRG_INFO *file = handler->myrg_info();
- MYRG_TABLE *table = file->open_tables;
- for(;table != file->end_table ; table++)
+ for (MYRG_TABLE *table = file->open_tables;
+ table != file->end_table ;
+ table++)
{
char key[MAX_DBKEY_LENGTH];
- uint key_length =
- Query_cache::filename_2_table_key(key, table->table->filename);
- n++;
- Query_cache_block_table * block_table = block->table(n);
- block_table->n=n;
+ uint key_length =filename_2_table_key(key, table->table->filename);
+ (++block_table)->n= ++n;
if (!insert_table(key_length, key, block_table,
Query_cache_table::type_convertion(DB_TYPE_MYISAM)))
goto err;
}
}
}
+
err:
- if (i != 0)
- {
- n--;
- DBUG_PRINT("info", ("filed at table %d", (int)n));
- TABLE_COUNTER_TYPE idx = 0;
- for(i = tables_used;
- idx < n;
- idx++)
- {
- unlink_table(block->table(n));
- }
+ 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);
}
- return(i == 0);
+ return (tables_used == 0);
}
-my_bool Query_cache::insert_table(uint key_len, char * key,
- Query_cache_block_table * node,
- Query_cache_table::query_cache_table_type
- type)
+/*
+ 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)
{
DBUG_ENTER("Query_cache::insert_table");
- DBUG_PRINT("info", ("insert table node 0x%lx, len %d",
+ DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
(ulong)node, key_len));
- Query_cache_block *table_block = (Query_cache_block *)
- hash_search(&tables, key, key_len);
+
+ Query_cache_block *table_block = ((Query_cache_block *)
+ hash_search(&tables, key, key_len));
if (table_block == 0)
{
- DBUG_PRINT("info", ("new table block from 0x%lx (%u)",
- (ulong)key, (int) key_len));
+ DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
+ (ulong) key, (int) key_len));
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)
{
- DBUG_PRINT("info", ("Can't write table name to cache"));
+ DBUG_PRINT("qcache", ("Can't write table name to cache"));
DBUG_RETURN(0);
}
- Query_cache_table * header = table_block->table();
+ Query_cache_table *header = table_block->table();
header->type(type);
double_linked_list_simple_include(table_block,
- tables_blocks[type]);
- Query_cache_block_table * list_root = table_block->table(0);
+ &tables_blocks[type]);
+ Query_cache_block_table *list_root = table_block->table(0);
list_root->n = 0;
list_root->next = list_root->prev = list_root;
- if (hash_insert(&tables, (const byte *)table_block))
+ if (hash_insert(&tables, (const byte *) table_block))
{
- DBUG_PRINT("info", ("Can't insert table to hash"));
+ DBUG_PRINT("qcache", ("Can't insert table to hash"));
// write_block_data return locked block
free_memory_block(table_block);
DBUG_RETURN(0);
}
- char * db = header->db();
+ char *db = header->db();
+ /*
+ TODO: Eliminate strlen() by sending this to the function
+ To do this we have to add db_len to the TABLE_LIST and TABLE structures.
+ */
header->table(db + strlen(db) + 1);
}
- 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;
+ 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;
node->parent = table_block->table();
DBUG_RETURN(1);
}
-void Query_cache::unlink_table(Query_cache_block_table * node)
+
+void Query_cache::unlink_table(Query_cache_block_table *node)
{
- node->prev->next = node->next; node->next->prev = node->prev;
- Query_cache_block_table * neighbour = node->next;
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ Query_cache_block_table *neighbour = node->next;
if (neighbour->next == neighbour)
{
- // list is empty (neighbour is root of list)
- Query_cache_block * table_block = neighbour->block();
+ // list is empty (neighbor is root of list)
+ Query_cache_block *table_block = neighbour->block();
double_linked_list_exclude(table_block,
- tables_blocks[table_block->table()->type()]);
+ &tables_blocks[table_block->table()->type()]);
hash_delete(&tables,(byte *) table_block);
free_memory_block(table_block);
}
}
/*****************************************************************************
- * free memory management
- *****************************************************************************/
+ Free memory management
+*****************************************************************************/
-Query_cache_block * Query_cache::allocate_block(ulong len,
- my_bool not_less,
- ulong min,
- my_bool under_guard)
+Query_cache_block *
+Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
+ my_bool under_guard)
{
DBUG_ENTER("Query_cache::allocate_n_lock_block");
- DBUG_PRINT("info", ("len %lu, not less %d, min %lu, uder_guard %d",
+ DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d",
len, not_less,min,under_guard));
if (len >= min(query_cache_size, query_cache_limit))
{
- DBUG_PRINT("info", ("Query cache hase only %lu memory and limit %lu",
+ DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu",
query_cache_size, query_cache_limit));
DBUG_RETURN(0); // in any case we don't have such piece of memory
}
- if (!under_guard){STRUCT_LOCK(&structure_guard_mutex);};
+ if (!under_guard)
+ STRUCT_LOCK(&structure_guard_mutex);
- Query_cache_block *block = get_free_block(len, not_less, min);
- while( block == 0 && free_old_query())
+ /* Free old queries until we have enough memory to store this block */
+ Query_cache_block *block;
+ do
{
- block = get_free_block(len, not_less, min);
+ block= get_free_block(len, not_less, min);
}
+ while (block == 0 && !free_old_query());
- if (block!=0)
+ if (block != 0) // If we found a suitable block
{
if (block->length > ALIGN_SIZE(len) + min_allocation_unit)
split_block(block,ALIGN_SIZE(len));
}
- if (!under_guard){STRUCT_UNLOCK(&structure_guard_mutex);};
+ if (!under_guard)
+ STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(block);
}
-Query_cache_block * Query_cache::get_free_block (ulong len,
- my_bool not_less,
- ulong min)
+
+Query_cache_block *
+Query_cache::get_free_block(ulong len, my_bool not_less, ulong min)
{
- DBUG_ENTER("Query_cache::get_free_block");
Query_cache_block *block = 0, *first = 0;
- DBUG_PRINT("info",("length %lu, not_less %d, min %lu", len,
+ DBUG_ENTER("Query_cache::get_free_block");
+ DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len,
(int)not_less, min));
- /* find block with minimal size > len */
-
+ /* Find block with minimal size > len */
uint start = find_bin(len);
// try matching bin
if (bins[start].number != 0)
{
- Query_cache_block * list = bins[start].free_blocks;
+ Query_cache_block *list = bins[start].free_blocks;
first = list;
- while(first->next != list && first->length < len)
- {
+ while (first->next != list && first->length < len)
first=first->next;
- }
if (first->length >= len)
block=first;
}
if (block == 0 && start > 0)
{
- DBUG_PRINT("info",("try bins whith more bigger blocks"));
- //try more big bins
+ DBUG_PRINT("qcache",("Try bins with bigger block size"));
+ // Try more big bins
int i = start - 1;
- while(i > 0 && bins[i].number == 0)
+ while (i > 0 && bins[i].number == 0)
i--;
if (bins[i].number > 0)
block = bins[i].free_blocks;
}
- // if no big blocks => try less size (if it is possible)
+
+ // If no big blocks => try less size (if it is possible)
if (block == 0 && ! not_less)
{
- DBUG_PRINT("info",("try smaller blocks"));
+ DBUG_PRINT("qcache",("Try to allocate a smaller block"));
if (first != 0 && first->length > min)
block = first;
else
{
uint i = start + 1;
- while( i < mem_bin_num && bins[i].number == 0)
- i++;
- if (i < mem_bin_num && bins[i].number > 0 &&
- bins[i].free_blocks->prev->length >= min)
+ /* 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)
block = bins[i].free_blocks->prev;
}
}
if (block != 0)
exclude_from_free_memory_list(block);
- DBUG_PRINT("info",("getting block 0x%lx", (ulong) block));
+ DBUG_PRINT("qcache",("getting block 0x%lx", (ulong) block));
DBUG_RETURN(block);
}
-void Query_cache::free_memory_block(Query_cache_block * block)
+
+void Query_cache::free_memory_block(Query_cache_block *block)
{
DBUG_ENTER("Query_cache::free_n_unlock_memory_block");
block->used=0;
- DBUG_PRINT("info",("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx",
+ DBUG_PRINT("qcache",("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx",
(ulong) first_block, (ulong) block,block->pnext,
(ulong) block->pprev));
+
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;
}
-void Query_cache::split_block(Query_cache_block * block,ulong len)
+
+void Query_cache::split_block(Query_cache_block *block,ulong len)
{
DBUG_ENTER("Query_cache::split_block");
- Query_cache_block * new_block = (Query_cache_block *)(((byte*)block)+len);
+ Query_cache_block *new_block = (Query_cache_block*)(((byte*) block)+len);
new_block->init(block->length - len);
block->length=len;
- new_block->pnext = block->pnext; block->pnext = new_block;
- new_block->pprev = block; new_block->pnext->pprev = new_block;
+ new_block->pnext = block->pnext;
+ block->pnext = new_block;
+ new_block->pprev = block;
+ new_block->pnext->pprev = new_block;
insert_into_free_memory_list(new_block);
- DBUG_PRINT("info", ("split 0x%lx (%lu) new 0x%lx",
+ DBUG_PRINT("qcache", ("split 0x%lx (%lu) new 0x%lx",
(ulong) block, len, (ulong) new_block));
DBUG_VOID_RETURN;
}
+
Query_cache_block *
Query_cache::join_free_blocks(Query_cache_block *first_block,
- Query_cache_block * block_in_list)
+ Query_cache_block *block_in_list)
{
+ Query_cache_block *second_block;
DBUG_ENTER("Query_cache::join_free_blocks");
- DBUG_PRINT("info",
+ DBUG_PRINT("qcache",
("join first 0x%lx, pnext 0x%lx, in list 0x%lx",
(ulong) first_block, (ulong) first_block->pnext,
(ulong) block_in_list));
- exclude_from_free_memory_list(block_in_list);
- Query_cache_block * second_block = first_block->pnext;
- // may be was not free block
- second_block->type = Query_cache_block::FREE;second_block->used=0;
+ exclude_from_free_memory_list(block_in_list);
+ second_block = first_block->pnext;
+ // May be was not free block
+ second_block->type = Query_cache_block::FREE;
+ second_block->used=0;
second_block->destroy();
first_block->length += second_block->length;
@@ -1935,136 +1986,135 @@ Query_cache::join_free_blocks(Query_cache_block *first_block,
DBUG_RETURN(first_block);
}
-my_bool Query_cache::append_next_free_block(Query_cache_block * block,
+
+my_bool Query_cache::append_next_free_block(Query_cache_block *block,
ulong add_size)
{
+ Query_cache_block *next_block = block->pnext;
DBUG_ENTER("Query_cache::append_next_free_block");
DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block,
add_size));
- Query_cache_block * next_block = block->pnext;
+
if (next_block->is_free())
{
- exclude_from_free_memory_list(next_block);
ulong old_len = block->length;
-
+ exclude_from_free_memory_list(next_block);
next_block->destroy();
block->length += next_block->length;
block->pnext = next_block->pnext;
next_block->pnext->pprev = block;
- if (block->length >
- ALIGN_SIZE(old_len + add_size) + min_allocation_unit)
+ if (block->length > ALIGN_SIZE(old_len + add_size) + min_allocation_unit)
split_block(block,ALIGN_SIZE(old_len + add_size));
DBUG_PRINT("exit", ("block was appended"));
DBUG_RETURN(1);
}
- else
- {
- DBUG_PRINT("exit", ("block was not appended"));
- DBUG_RETURN(0);
- }
+ DBUG_RETURN(0);
}
-void Query_cache::exclude_from_free_memory_list(Query_cache_block * free_block)
+
+void Query_cache::exclude_from_free_memory_list(Query_cache_block *free_block)
{
DBUG_ENTER("Query_cache::exclude_from_free_memory_list");
- Query_cache_memory_bin * bin = *((Query_cache_memory_bin **)
- free_block->data());
- double_linked_list_exclude(free_block ,bin->free_blocks);
+ Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
+ free_block->data());
+ double_linked_list_exclude(free_block, &bin->free_blocks);
bin->number--;
free_memory-=free_block->length;
- DBUG_PRINT("info",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block,
- (ulong)bin));
+ DBUG_PRINT("qcache",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block,
+ (ulong) bin));
DBUG_VOID_RETURN;
}
-void Query_cache::insert_into_free_memory_list(Query_cache_block * free_block)
+void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block)
{
DBUG_ENTER("Query_cache::insert_into_free_memory_list");
uint idx = find_bin(free_block->length);
- insert_into_free_memory_sorted_list(free_block, bins[idx].free_blocks);
+ insert_into_free_memory_sorted_list(free_block, &bins[idx].free_blocks);
/*
- we have enough memory in block for storing bin reference due to
+ We have enough memory in block for storing bin reference due to
min_allocation_unit choice
*/
- Query_cache_memory_bin * *bin_ptr = (Query_cache_memory_bin**)
- free_block->data();
- *bin_ptr = &(bins[idx]);
- bins[idx].number++;
- DBUG_PRINT("info",("insert block 0x%lx, bin[%d] 0x%lx",
- (ulong) free_block, idx,
- (ulong) &(bins[idx])));
+ 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));
DBUG_VOID_RETURN;
}
uint Query_cache::find_bin(ulong size)
{
+ int i;
DBUG_ENTER("Query_cache::find_bin");
- //begin small blocks to big (small blocks frequently asked)
- int i = mem_bin_steps - 1;
- for(; i > 0 && steps[i-1].size < size; i--);
+ // Begin small blocks to big (small blocks frequently asked)
+ for (i=mem_bin_steps - 1; i > 0 && steps[i-1].size < size; i--) ;
if (i == 0)
{
// first bin not subordinate of common rules
- DBUG_PRINT("info", ("first bin (# 0), size %lu",size));
+ DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size));
DBUG_RETURN(0);
}
uint bin = steps[i].idx - (uint)((size - steps[i].size)/steps[i].increment);
- DBUG_PRINT("info", ("bin %u step %u, size %lu", bin, i, size));
+ DBUG_PRINT("qcache", ("bin %u step %u, size %lu", bin, i, size));
DBUG_RETURN(bin);
}
+
/*****************************************************************************
- * lists management
- *****************************************************************************/
+ Lists management
+*****************************************************************************/
-void Query_cache::move_to_query_list_end(Query_cache_block * query_block)
+void Query_cache::move_to_query_list_end(Query_cache_block *query_block)
{
DBUG_ENTER("Query_cache::move_to_query_list_end");
- double_linked_list_exclude(query_block, queries_blocks);
- double_linked_list_simple_include(query_block, queries_blocks);
+ double_linked_list_exclude(query_block, &queries_blocks);
+ double_linked_list_simple_include(query_block, &queries_blocks);
DBUG_VOID_RETURN;
}
+
void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block *
new_block,
- Query_cache_block *
- &list)
+ Query_cache_block **
+ list)
{
DBUG_ENTER("Query_cache::insert_into_free_memory_sorted_list");
/*
list sorted by size in ascendant order, because we need small blocks
- frequently than big one
+ more frequently than bigger ones
*/
new_block->used = 0;
new_block->n_tables = 0;
new_block->type = Query_cache_block::FREE;
- if (list == 0)
+ if (*list == 0)
{
- list = new_block->next=new_block->prev=new_block;
- DBUG_PRINT("info", ("inserted into empty list"));
+ *list = new_block->next=new_block->prev=new_block;
+ DBUG_PRINT("qcache", ("inserted into empty list"));
}
else
{
- Query_cache_block *point = list;
+ Query_cache_block *point = *list;
if (point->length >= new_block->length)
{
point = point->prev;
- list = new_block;
+ *list = new_block;
}
else
{
- while(point->next != list &&
- point->next->length < new_block->length)
- {
+ /* Find right position in sorted list to put block */
+ while (point->next != *list &&
+ point->next->length < new_block->length)
point=point->next;
- }
}
- new_block->prev = point; new_block->next = point->next;
- new_block->next->prev = new_block; point->next = new_block;
+ new_block->prev = point;
+ new_block->next = point->next;
+ new_block->next->prev = new_block;
+ point->next = new_block;
}
free_memory+=new_block->length;
DBUG_VOID_RETURN;
@@ -2072,48 +2122,50 @@ void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block *
void
-Query_cache::double_linked_list_simple_include(Query_cache_block * point,
- Query_cache_block *
- &list_pointer)
+Query_cache::double_linked_list_simple_include(Query_cache_block *point,
+ Query_cache_block **
+ list_pointer)
{
DBUG_ENTER("Query_cache::double_linked_list_simple_include");
- DBUG_PRINT("info", ("including block 0x%lx", (ulong) point));
- if (list_pointer == 0)
- list_pointer=point->next=point->prev=point;
+ DBUG_PRINT("qcache", ("including block 0x%lx", (ulong) point));
+ if (*list_pointer == 0)
+ *list_pointer=point->next=point->prev=point;
else
{
- point->next = list_pointer;
- point->prev = list_pointer->prev;
+ point->next = (*list_pointer);
+ point->prev = (*list_pointer)->prev;
point->prev->next = point;
- list_pointer->prev = point;
- list_pointer = point;
+ (*list_pointer)->prev = point;
+ (*list_pointer) = point;
}
DBUG_VOID_RETURN;
}
void
Query_cache::double_linked_list_exclude(Query_cache_block *point,
- Query_cache_block * &list_pointer)
+ Query_cache_block **list_pointer)
{
DBUG_ENTER("Query_cache::double_linked_list_exclude");
- DBUG_PRINT("info", ("excluding block 0x%lx, list 0x%lx",
+ DBUG_PRINT("qcache", ("excluding block 0x%lx, list 0x%lx",
(ulong) point, (ulong) list_pointer));
if (point->next == point)
- list_pointer = 0; // empty list
+ *list_pointer = 0; // empty list
else
{
point->next->prev = point->prev;
point->prev->next = point->next;
- if (point == list_pointer) list_pointer = point->next;
+ if (point == *list_pointer)
+ *list_pointer = point->next;
}
DBUG_VOID_RETURN;
}
+
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,
- *tail_tail = tail_head->prev;
+ *tail_tail = tail_head->prev;
head_head->prev = tail_tail;
head_tail->next = tail_head;
tail_head->prev = head_tail;
@@ -2121,72 +2173,68 @@ void Query_cache::double_linked_list_join(Query_cache_block *head_tail,
}
/*****************************************************************************
- * query
- *****************************************************************************/
+ Query
+*****************************************************************************/
/*
- if query is cachable return numder tables in query
- (query whithout tables tot chached)
+ if query is cacheable return number tables in query
+ (query without tables are not cached)
*/
-TABLE_COUNTER_TYPE Query_cache::is_cachable(THD *thd,
- uint query_len, char *query,
- LEX *lex, TABLE_LIST* tables_used)
+
+TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len,
+ char *query,
+ LEX *lex, TABLE_LIST *tables_used)
{
TABLE_COUNTER_TYPE tables = 0;
- DBUG_ENTER("Query_cache::is_cachable");
+ DBUG_ENTER("Query_cache::is_cacheable");
+
if (lex->sql_command == SQLCOM_SELECT &&
thd->temporary_tables == 0 &&
- (thd->query_cache_type == 1 || (thd->query_cache_type == 2 &&
- (lex->select->options &
+ (thd->query_cache_type == 1 ||
+ (thd->query_cache_type == 2 && (lex->select->options &
OPTION_TO_QUERY_CACHE))) &&
thd->safe_to_cache_query)
{
- DBUG_PRINT("info", ("options %lx %lx, type %u",
+ my_bool has_transactions = 0;
+ DBUG_PRINT("qcache", ("options %lx %lx, type %u",
OPTION_TO_QUERY_CACHE,
lex->select->options,
(int) thd->query_cache_type));
- my_bool has_transactions = 0;
- TABLE_LIST * i = tables_used;
- for(; i != 0; i=i->next)
+
+ for (; tables_used; tables_used=tables_used->next)
{
tables++;
- DBUG_PRINT("info", ("table %s, db %s, type %u", i->real_name,
- i->db, i->table->db_type));
+ DBUG_PRINT("qcache", ("table %s, db %s, type %u",
+ tables_used->real_name,
+ tables_used->db, tables_used->table->db_type));
has_transactions = (has_transactions ||
- i->table->file->has_transactions());
- if (i->table->db_type == DB_TYPE_MRG_ISAM)
+ tables_used->table->file->has_transactions());
+
+ if (tables_used->table->db_type == DB_TYPE_MRG_ISAM)
{
- DBUG_PRINT("info", ("select not cachable: used MRG_ISAM table(s)"));
+ DBUG_PRINT("qcache", ("select not cacheable: used MRG_ISAM table(s)"));
DBUG_RETURN(0);
}
- if (i->table->db_type == DB_TYPE_MRG_MYISAM)
+ if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM)
{
- ha_myisammrg * handler = (ha_myisammrg *)i->table->file;
+ ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file;
MYRG_INFO *file = handler->myrg_info();
- MYRG_TABLE *table = file->open_tables;
- for(;table != file->end_table ; table++)
- {
- tables++;
- DBUG_PRINT("loop", (" + 1 table (mrg_MyISAM)"));
- }
+ tables+= (file->end_table - file->open_tables);
}
}
if ((thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)) &&
has_transactions)
{
- DBUG_PRINT("info", ("not in autocommin mode"));
+ DBUG_PRINT("qcache", ("not in autocommin mode"));
DBUG_RETURN(0);
}
- else
- {
- DBUG_PRINT("info", ("select have %d tables", tables));
- DBUG_RETURN(tables);
- }
+ DBUG_PRINT("qcache", ("select is using %d tables", tables));
+ DBUG_RETURN(tables);
}
- DBUG_PRINT("info",
- ("not interest query: %d or not cachable, \
-options %lx %lx, type %u",
+
+ DBUG_PRINT("qcache",
+ ("not interesting query: %d or not cacheable, options %lx %lx, type %u",
(int) lex->sql_command,
OPTION_TO_QUERY_CACHE,
lex->select->options,
@@ -2194,31 +2242,36 @@ options %lx %lx, type %u",
DBUG_RETURN(0);
}
+
/*****************************************************************************
- * packing
- *****************************************************************************/
+ Packing
+*****************************************************************************/
void Query_cache::pack_cache()
{
- DBUG_ENTER("Query_cache::pack_cache");
STRUCT_LOCK(&structure_guard_mutex);
- DUMP(this);
- byte * border = 0;
- Query_cache_block * before = 0;
+ byte *border = 0;
+ Query_cache_block *before = 0;
ulong gap = 0;
my_bool ok = 1;
- Query_cache_block * i = first_block;
+ Query_cache_block *i = first_block;
+ DBUG_ENTER("Query_cache::pack_cache");
+ DUMP(this);
+
do
{
- ok = move_by_type(border, before, gap, i);
+ ok = move_by_type(&border, &before, &gap, i);
i = i->pnext;
} while (ok && i != first_block);
+
if (border != 0)
{
- Query_cache_block * new_block = (Query_cache_block *) border;
+ Query_cache_block *new_block = (Query_cache_block *) border;
new_block->init(gap);
- new_block->pnext = before->pnext; before->pnext = new_block;
- new_block->pprev = before; new_block->pnext->pprev = new_block;
+ 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);
@@ -2226,219 +2279,225 @@ void Query_cache::pack_cache()
DBUG_VOID_RETURN;
}
-my_bool Query_cache::move_by_type(byte * &border,
- Query_cache_block * &before, ulong &gap, Query_cache_block * i)
+
+my_bool Query_cache::move_by_type(byte **border,
+ Query_cache_block **before, ulong *gap,
+ Query_cache_block *block)
{
DBUG_ENTER("Query_cache::move_by_type");
+
my_bool ok = 1;
- switch(i->type)
- {
+ switch (block->type) {
case Query_cache_block::FREE:
+ {
+ DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block));
+ if (*border == 0)
{
- DBUG_PRINT("info", ("block 0x%lx FREE", (ulong) i));
- if (border == 0)
- {
- border = (byte *) i;
- before = i->pprev;
- DBUG_PRINT("info", ("gap begining here"));
- }
- exclude_from_free_memory_list(i);
- gap +=i->length;
- i->pprev->pnext=i->pnext;
- i->pnext->pprev=i->pprev;
- i->destroy();
- DBUG_PRINT("info", ("added to gap (%lu)", gap));
- break;
+ *border = (byte *) block;
+ *before = block->pprev;
+ DBUG_PRINT("qcache", ("gap beginning here"));
}
+ exclude_from_free_memory_list(block);
+ *gap +=block->length;
+ block->pprev->pnext=block->pnext;
+ block->pnext->pprev=block->pprev;
+ block->destroy();
+ DBUG_PRINT("qcache", ("added to gap (%lu)", *gap));
+ break;
+ }
case Query_cache_block::TABLE:
- {
- DBUG_PRINT("info", ("block 0x%lx TABLE", (ulong) i));
- if (border == 0) break;
- ulong len = i->length,
- used = i->used;
- Query_cache_block_table * list_root = i->table(0);
- Query_cache_block_table
- *tprev = list_root->prev,
- *tnext = list_root->next;
- Query_cache_block
- *prev = i->prev,
- *next = i->next,
- *pprev = i->pprev,
- *pnext = i->pnext,
- *new_block =(Query_cache_block *) border;
- char * data = (char*)i->data();
- hash_delete(&tables, (byte *)i);
- i->destroy();
- new_block->init(len);
- new_block->type=Query_cache_block::TABLE;
- new_block->used=used;
- new_block->n_tables=1;
- memcpy((char*)new_block->data(), data,
- len-new_block->headers_len());
- relink(i, new_block, next, prev, pnext, pprev);
- if (tables_blocks[new_block->table()->type()] == i)
- {
- tables_blocks[new_block->table()->type()] = new_block;
- }
- Query_cache_block_table * nlist_root = new_block->table(0);
- nlist_root->n = 0;
- nlist_root->next = (tnext==list_root?nlist_root:tnext);
- nlist_root->prev = (tprev==list_root?nlist_root:tnext);
- tnext->prev = list_root;
- tprev->next = list_root;
- border += len;
- before = new_block;
- hash_insert(&tables, (const byte *)new_block);
- DBUG_PRINT("info", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
- len, (ulong) new_block, (ulong) border));
+ {
+ DBUG_PRINT("qcache", ("block 0x%lx TABLE", (ulong) block));
+ if (*border == 0)
break;
- }
+ 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;
+ char *data = (char*) block->data();
+ byte *key;
+ uint key_length;
+ key=query_cache_table_get_key((byte*) block, &key_length,0);
+ hash_search(&tables, key, key_length);
+
+ block->destroy();
+ new_block->init(len);
+ new_block->type=Query_cache_block::TABLE;
+ new_block->used=used;
+ new_block->n_tables=1;
+ memcpy((char*) new_block->data(), data, len-new_block->headers_len());
+ 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;
+ nlist_root->next = (tnext == list_root ? nlist_root : tnext);
+ nlist_root->prev = (tprev == list_root ? nlist_root: tnext);
+ tnext->prev = list_root;
+ tprev->next = list_root;
+ *border += len;
+ *before = new_block;
+ /* 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;
+ }
case Query_cache_block::QUERY:
+ {
+ 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;
+ key=query_cache_query_get_key((byte*) block, &key_length,0);
+ hash_search(&queries, key, key_length);
+
+ memcpy((char*) new_block->table(0), (char*) block->table(0),
+ 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;
+ memcpy((char*) new_block->data(), data, len - new_block->headers_len());
+ 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++)
{
- DBUG_PRINT("info", ("block 0x%lx QUERY", (ulong) i));
- if (border == 0) break;
- BLOCK_LOCK_WR(i);
- ulong len = i->length,
- used = i->used;
- TABLE_COUNTER_TYPE n_tables = i->n_tables;
- Query_cache_block
- *prev = i->prev,
- *next = i->next,
- *pprev = i->pprev,
- *pnext = i->pnext,
- *new_block =(Query_cache_block *) border;
- char * data = (char*)i->data();
- Query_cache_block * first_result_block = ((Query_cache_query *)
- i->data())->result();
- hash_delete(&queries, (byte *)i);
- memcpy((char*)new_block->table(0), (char*)i->table(0),
- n_tables * ALIGN_SIZE(sizeof(Query_cache_block_table)));
- i->query()->unlock_n_destroy();
- i->destroy();
- new_block->init(len);
- new_block->type=Query_cache_block::QUERY;
- new_block->used=used;
- new_block->n_tables=n_tables;
- memcpy((char*)new_block->data(), data,
- len - new_block->headers_len());
- relink(i, new_block, next, prev, pnext, pprev);
- if (queries_blocks == i)
- queries_blocks = new_block;
- TABLE_COUNTER_TYPE j=0;
- for(; j< n_tables; j++)
- {
- Query_cache_block_table * block_table = new_block->table(j);
- block_table->next->prev = block_table;
- block_table->prev->next = block_table;
- }
- DBUG_PRINT("info", ("after cicle 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
- {
- result_block->result()->parent(new_block);
- result_block = result_block->next;
- } while ( result_block != first_result_block );
- }
- NET * net = new_block->query()->writer();
- if (net != 0)
+ 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
{
- net->query_cache_query = (gptr) new_block;
- }
- hash_insert(&queries, (const byte *)new_block);
- DBUG_PRINT("info", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
- len, (ulong) new_block, (ulong) border));
- break;
+ result_block->result()->parent(new_block);
+ result_block = result_block->next;
+ } while ( result_block != first_result_block );
}
+ NET *net = new_block->query()->writer();
+ if (net != 0)
+ {
+ net->query_cache_query = (gptr) new_block;
+ }
+ /* 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;
+ }
case Query_cache_block::RES_INCOMPLETE:
case Query_cache_block::RES_BEG:
case Query_cache_block::RES_CONT:
case Query_cache_block::RESULT:
- {
- DBUG_PRINT("info", ("block 0x%lx RES* (%d)", (ulong) i, (int)i->type));
- if (border == 0) break;
- Query_cache_block
- *query_block = i->result()->parent(),
- *next = i->next,
- *prev = i->prev;
- Query_cache_block::block_type type = i->type;
- BLOCK_LOCK_WR(query_block);
- ulong len = i->length,
- used = i->used;
- Query_cache_block
- *pprev = i->pprev,
- *pnext = i->pnext,
- *new_block =(Query_cache_block *) border;
- char * data = (char*)i->data();
- i->destroy();
- new_block->init(len);
- new_block->type=type;
- new_block->used=used;
- memcpy((char*)new_block->data(), data,
- len - new_block->headers_len());
- relink(i, new_block, next, prev, pnext, pprev);
- new_block->result()->parent(query_block);
- Query_cache_query * query = query_block->query();
- if (query->result() == i)
- 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;
- }
- BLOCK_UNLOCK_WR(query_block);
- DBUG_PRINT("info", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
- len, (ulong) new_block, (ulong) border));
+ {
+ DBUG_PRINT("qcache", ("block 0x%lx RES* (%d)", (ulong) block,
+ (int) block->type));
+ if (*border == 0)
break;
+ 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;
+ memcpy((char*) new_block->data(), data, len - new_block->headers_len());
+ 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;
}
+ 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;
+ }
default:
- DBUG_PRINT("error", ("unexpectet block type %d, block 0x%lx",
- (int)i->type, (ulong) i));
+ DBUG_PRINT("error", ("unexpected block type %d, block 0x%lx",
+ (int)block->type, (ulong) block));
ok = 0;
}
DBUG_RETURN(ok);
}
-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)
+
+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)
{
- nblock->prev = (prev==oblock?nblock:prev); //check pointer to himself
- nblock->next = (next==oblock?nblock:next);
+ nblock->prev = (prev == oblock ? nblock : prev); //check pointer to himself
+ nblock->next = (next == oblock ? nblock : next);
prev->next=nblock;
next->prev=nblock;
- nblock->pprev = pprev; //physical pointer to himself have only 1 free block
+ nblock->pprev = pprev; // Physical pointer to himself have only 1 free block
nblock->pnext = pnext;
pprev->pnext=nblock;
pnext->pprev=nblock;
}
+
my_bool Query_cache::join_results(ulong join_limit)
{
//TODO
- DBUG_ENTER("Query_cache::join_results");
my_bool has_moving = 0;
- Query_cache_block *query_block = 0;
+ DBUG_ENTER("Query_cache::join_results");
+
STRUCT_LOCK(&structure_guard_mutex);
if (queries_blocks != 0)
{
- Query_cache_block *i = queries_blocks;
+ Query_cache_block *block = queries_blocks;
do
{
- Query_cache_query * header = i->query();
+ Query_cache_query *header = block->query();
if (header->result() != 0 &&
header->result()->type == Query_cache_block::RESULT &&
header->length() > join_limit)
@@ -2451,32 +2510,32 @@ my_bool Query_cache::join_results(ulong join_limit)
{
has_moving = 1;
Query_cache_block *first_result = header->result();
- ulong new_len = header->length() +
- ALIGN_SIZE(sizeof(Query_cache_block)) +
- ALIGN_SIZE(sizeof(Query_cache_result));
+ ulong new_len = (header->length() +
+ ALIGN_SIZE(sizeof(Query_cache_block)) +
+ ALIGN_SIZE(sizeof(Query_cache_result)));
if (new_result_block->length >
ALIGN_SIZE(new_len) + min_allocation_unit)
- split_block(new_result_block,ALIGN_SIZE(new_len));
- BLOCK_LOCK_WR(i);
+ split_block(new_result_block, ALIGN_SIZE(new_len));
+ BLOCK_LOCK_WR(block);
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;
- DBUG_PRINT("info", ("new block %lu/%lu (%lu)",
+ DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)",
new_result_block->length,
new_result_block->used,
header->length()));
Query_cache_result *new_result = new_result_block->result();
- new_result->parent(i);
+ new_result->parent(block);
byte *write_to = (byte*) new_result->data();
Query_cache_block *result_block = first_result;
do
{
- ulong len = result_block->used - result_block->headers_len() -
- ALIGN_SIZE(sizeof(Query_cache_result));
+ ulong len = (result_block->used - result_block->headers_len() -
+ ALIGN_SIZE(sizeof(Query_cache_result)));
DBUG_PRINT("loop", ("add block %lu/%lu (%lu)",
result_block->length,
result_block->used,
@@ -2485,36 +2544,197 @@ my_bool Query_cache::join_results(ulong join_limit)
(char*) result_block->result()->data(),
len);
write_to += len;
- Query_cache_block * old_result_block = result_block;
+ Query_cache_block *old_result_block = result_block;
result_block = result_block->next;
free_memory_block(old_result_block);
} while (result_block != first_result);
- BLOCK_UNLOCK_WR(i);
+ BLOCK_UNLOCK_WR(block);
}
}
- i = i->next;
- } while ( i != queries_blocks );
+ block = block->next;
+ } while ( block != queries_blocks );
}
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(has_moving);
}
-uint Query_cache::filename_2_table_key (char * key, char *filename)
+
+uint Query_cache::filename_2_table_key (char *key, const char *path)
{
- DBUG_ENTER("Query_cache::filename_2_table_key");
- char tablename[FN_REFLEN];
- char dbbuff[FN_REFLEN];
- char *dbname;
+ char tablename[FN_REFLEN+2], *filename, *dbname;
Query_cache_block *table_block;
- fn_format(tablename,filename,"","",3);
+ 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--) ;
+ db_length= (filename - dbname) - 1;
+ DBUG_PRINT("qcache", ("table '%-.*s.%s'", db_length, dbname, filename));
+
+ DBUG_RETURN((uint) (strmov(strnmov(key, dbname, db_length) + 1,
+ filename) -key) + 1);
+}
+
+
+/****************************************************************************
+ Functions to be used when debugging
+****************************************************************************/
+
+#ifndef DBUG_OFF
+
+void Query_cache::wreck(uint line, const char *message)
+{
+ 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", ("=================================="));
+ current_thd->killed = 1;
+ bins_dump();
+ cache_dump();
+ DBUG_VOID_RETURN;
+}
+
+
+void Query_cache::bins_dump()
+{
+ uint i;
+ 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", ("-------------------------"));
+}
- int path_len = (strrchr(filename, '/') - filename);
- strncpy(dbbuff, filename, path_len);
- dbbuff[path_len] = '\0';
- dbname = strrchr(dbbuff, '/') + 1;
- DBUG_PRINT("info", ("table %s.%s", dbname, tablename));
+void Query_cache::cache_dump()
+{
+ 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", ("-------------------------------------"));
+}
+
- DBUG_RETURN( (uint) (strmov(strmov(key, dbname) + 1,
- tablename) -key) + 1);
+void Query_cache::queries_dump()
+{
+ 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);
+ byte flags = (byte) str[len-1];
+ DBUG_PRINT("qcache", ("%u (%u,%u) %.*s",len,
+ ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0),
+ (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len,
+ str));
+ 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));
+
+ 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", ("------------------"));
+}
+
+
+void Query_cache::tables_dump()
+{
+ 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", ("--------------------"));
}
+#endif /* DBUG_OFF */