sql_cache.cc 131 KB
Newer Older
1
/* Copyright (C) 2000 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7 8 9 10
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
11

bk@work.mysql.com's avatar
bk@work.mysql.com committed
12 13 14 15
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
/*
  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).
202

203
7. Packing cache.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

Query cache packing is divided into two operation:
	- pack_cache
	- join_results

pack_cache moved all blocks to "top" of cache and create one block of free
space at the "bottom":

 before pack_cache    after pack_cache
 +-------------+      +-------------+
 | query 1     |      | query 1     |
 +-------------+      +-------------+
 | table 1     |      | table 1     |
 +-------------+      +-------------+
 | results 1.1 |      | results 1.1 |
 +-------------+      +-------------+
 | free        |      | query 2     |
 +-------------+      +-------------+
 | query 2     |      | table 2     |
 +-------------+ ---> +-------------+
 | table 2     |      | results 1.2 |
 +-------------+      +-------------+
 | results 1.2 |      | results 2   |
 +-------------+      +-------------+
 | free        |      | free        |
 +-------------+      |             |
 | results 2   |      |             |
 +-------------+      |             |
 | free        |      |             |
 +-------------+      +-------------+

pack_cache scan blocks in physical address order and move every non-free
block "higher".

pack_cach remove every free block it finds. The length of the deleted block
is accumulated to the "gap". All non free blocks should be shifted with the
"gap" step.

join_results scans all complete queries. If the results of query are not
stored in the same block, join_results tries to move results so, that they
are stored in one block.

 before join_results  after join_results
 +-------------+      +-------------+
 | query 1     |      | query 1     |
 +-------------+      +-------------+
 | table 1     |      | table 1     |
 +-------------+      +-------------+
 | results 1.1 |      | free        |
 +-------------+      +-------------+
 | query 2     |      | query 2     |
 +-------------+      +-------------+
 | table 2     |      | table 2     |
 +-------------+ ---> +-------------+
 | results 1.2 |      | free        |
 +-------------+      +-------------+
 | results 2   |      | results 2   |
 +-------------+      +-------------+
 | free        |      | results 1   |
 |             |      |             |
 |             |      +-------------+
 |             |      | free        |
 |             |      |             |
 +-------------+      +-------------+

If join_results allocated new block(s) then we need call pack_cache again.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
270 271 272 273 274 275 276 277 278 279

TODO list:

  - Delayed till after-parsing qache answer (for column rights processing)
  - Optimize cache resizing
      - if new_size < old_size then pack & shrink
      - if new_size > old_size copy cached query to new cache
  - Move MRG_MYISAM table type processing to handlers, something like:
        tables_used->table->file->register_used_filenames(callback,
                                                          first_argument);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
280 281 282 283 284 285 286 287 288 289
  - QC improvement suggested by Monty:
    - Add a counter in open_table() for how many MERGE (ISAM or MyISAM)
      tables are cached in the table cache.
      (This will be trivial when we have the new table cache in place I
      have been working on)
    - After this we can add the following test around the for loop in
      is_cacheable::

      if (thd->temp_tables || global_merge_table_count)

290
    - Another option would be to set thd->lex->safe_to_cache_query to 0
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
291 292 293
      in 'get_lock_data' if any of the tables was a tmp table or a
      MRG_ISAM table.
      (This could be done with almost no speed penalty)
294 295
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
296
#include "mysql_priv.h"
297
#ifdef HAVE_QUERY_CACHE
bk@work.mysql.com's avatar
bk@work.mysql.com committed
298 299 300
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
301
#include "../storage/myisammrg/ha_myisammrg.h"
302
#include "../storage/myisammrg/myrg_def.h"
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
303

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
304 305 306 307
#ifdef EMBEDDED_LIBRARY
#include "emb_qcache.h"
#endif

308
#if !defined(EXTRA_DBUG) && !defined(DBUG_OFF)
309
#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
310
  pthread_mutex_lock(M);}
311
#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
312
  (ulong)(M))); pthread_mutex_unlock(M);}
313
#define RW_WLOCK(M) {DBUG_PRINT("lock", ("rwlock wlock 0x%lx",(ulong)(M))); \
serg@serg.mylan's avatar
serg@serg.mylan committed
314
  if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")); \
315 316
  else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_RLOCK(M) {DBUG_PRINT("lock", ("rwlock rlock 0x%lx", (ulong)(M))); \
serg@serg.mylan's avatar
serg@serg.mylan committed
317
  if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")); \
318
  else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
319
#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \
serg@serg.mylan's avatar
serg@serg.mylan committed
320
  if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
321
  else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); }
322 323
#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
  pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
324
#define STRUCT_UNLOCK(M) { \
325 326 327
  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",\
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
328 329
  __LINE__,(ulong)(B))); \
  B->query()->lock_writing();}
330
#define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
331 332 333
  __LINE__,(ulong)(B))); \
  B->query()->lock_reading();}
#define BLOCK_UNLOCK_WR(B) { \
334
  DBUG_PRINT("lock", ("%d UNLOCK_WR 0x%lx",\
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
335 336
  __LINE__,(ulong)(B)));B->query()->unlock_writing();}
#define BLOCK_UNLOCK_RD(B) { \
337
  DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
338
  __LINE__,(ulong)(B)));B->query()->unlock_reading();}
339 340
#define DUMP(C) DBUG_EXECUTE("qcache", {\
  (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();})
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
341 342 343
#else
#define MUTEX_LOCK(M) pthread_mutex_lock(M)
#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M)
344 345 346
#define RW_WLOCK(M) rw_wrlock(M)
#define RW_RLOCK(M) rw_rdlock(M)
#define RW_UNLOCK(M) rw_unlock(M)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
347 348 349 350 351 352 353 354 355
#define STRUCT_LOCK(M) pthread_mutex_lock(M)
#define STRUCT_UNLOCK(M) pthread_mutex_unlock(M)
#define BLOCK_LOCK_WR(B) B->query()->lock_writing()
#define BLOCK_LOCK_RD(B) B->query()->lock_reading()
#define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing()
#define BLOCK_UNLOCK_RD(B) B->query()->unlock_reading()
#define DUMP(C)
#endif

356 357 358
const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS };
TYPELIB query_cache_type_typelib=
{
359
  array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL
360 361
};

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
362
/*****************************************************************************
363 364
 Query_cache_block_table method(s)
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
365 366 367

inline Query_cache_block * Query_cache_block_table::block()
{
368
  return (Query_cache_block *)(((uchar*)this) -
369
			       ALIGN_SIZE(sizeof(Query_cache_block_table)*n) -
370
			       ALIGN_SIZE(sizeof(Query_cache_block)));
371
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
372 373

/*****************************************************************************
374 375
   Query_cache_block method(s)
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
376 377 378 379

void Query_cache_block::init(ulong block_length)
{
  DBUG_ENTER("Query_cache_block::init");
380
  DBUG_PRINT("qcache", ("init block: 0x%lx  length: %lu", (ulong) this,
381
			block_length));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
382 383 384 385 386 387 388 389 390 391
  length = block_length;
  used = 0;
  type = Query_cache_block::FREE;
  n_tables = 0;
  DBUG_VOID_RETURN;
}

void Query_cache_block::destroy()
{
  DBUG_ENTER("Query_cache_block::destroy");
392 393
  DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d",
			(ulong) this, type));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
394 395 396 397 398 399
  type = INCOMPLETE;
  DBUG_VOID_RETURN;
}

inline uint Query_cache_block::headers_len()
{
400
  return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) +
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
401 402 403
	  ALIGN_SIZE(sizeof(Query_cache_block)));
}

404
inline uchar* Query_cache_block::data(void)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
405
{
406
  return (uchar*)( ((uchar*)this) + headers_len() );
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
}

inline Query_cache_query * Query_cache_block::query()
{
#ifndef DBUG_OFF
  if (type != QUERY)
    query_cache.wreck(__LINE__, "incorrect block type");
#endif
  return (Query_cache_query *) data();
}

inline Query_cache_table * Query_cache_block::table()
{
#ifndef DBUG_OFF
  if (type != TABLE)
    query_cache.wreck(__LINE__, "incorrect block type");
#endif
  return (Query_cache_table *) data();
}

inline Query_cache_result * Query_cache_block::result()
{
#ifndef DBUG_OFF
  if (type != RESULT && type != RES_CONT && type != RES_BEG &&
      type != RES_INCOMPLETE)
    query_cache.wreck(__LINE__, "incorrect block type");
#endif
  return (Query_cache_result *) data();
}

inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n)
{
  return ((Query_cache_block_table *)
440
	  (((uchar*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) +
441
	   n*sizeof(Query_cache_block_table)));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
442 443 444 445 446 447 448
}


/*****************************************************************************
 *   Query_cache_table method(s)
 *****************************************************************************/

449 450
extern "C"
{
451
uchar *query_cache_table_get_key(const uchar *record, size_t *length,
452
				my_bool not_used __attribute__((unused)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
453 454 455 456
{
  Query_cache_block* table_block = (Query_cache_block*) record;
  *length = (table_block->used - table_block->headers_len() -
	     ALIGN_SIZE(sizeof(Query_cache_table)));
457
  return (((uchar *) table_block->data()) +
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
458 459 460 461 462
	  ALIGN_SIZE(sizeof(Query_cache_table)));
}
}

/*****************************************************************************
463 464
    Query_cache_query methods
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
465 466

/*
467
   Following methods work for block read/write locking only in this
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
468 469
   particular case and in interaction with structure_guard_mutex.

470
   Lock for write prevents any other locking. (exclusive use)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
471 472 473
   Lock for read prevents only locking for write.
*/

474
inline void Query_cache_query::lock_writing()
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
475
{
476
  RW_WLOCK(&lock);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
477 478 479 480 481
}


/*
  Needed for finding queries, that we may delete from cache.
482 483 484
  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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
485 486 487 488 489
*/

my_bool Query_cache_query::try_lock_writing()
{
  DBUG_ENTER("Query_cache_block::try_lock_writing");
490
  if (rw_trywrlock(&lock)!=0)
491
  {
492
    DBUG_PRINT("info", ("can't lock rwlock"));
493 494
    DBUG_RETURN(0);
  }
495
  DBUG_PRINT("info", ("rwlock 0x%lx locked", (ulong) &lock));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
496 497 498 499
  DBUG_RETURN(1);
}


500
inline void Query_cache_query::lock_reading()
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
501
{
502
  RW_RLOCK(&lock);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
503 504 505
}


506
inline void Query_cache_query::unlock_writing()
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
507
{
508
  RW_UNLOCK(&lock);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
509 510 511
}


512
inline void Query_cache_query::unlock_reading()
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
513
{
514
  RW_UNLOCK(&lock);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
515 516
}

517 518 519 520 521 522 523 524

void Query_cache_query::init_n_lock()
{
  DBUG_ENTER("Query_cache_query::init_n_lock");
  res=0; wri = 0; len = 0;
  my_rwlock_init(&lock, NULL);
  lock_writing();
  DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx",
525
			(long) (((uchar*) this) -
526
                                ALIGN_SIZE(sizeof(Query_cache_block)))));
527 528 529 530 531 532 533 534
  DBUG_VOID_RETURN;
}


void Query_cache_query::unlock_n_destroy()
{
  DBUG_ENTER("Query_cache_query::unlock_n_destroy");
  DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx",
535
			(long) (((uchar*) this) -
536
                                ALIGN_SIZE(sizeof(Query_cache_block)))));
537 538 539 540 541 542 543 544 545 546
  /*
    The following call is not needed on system where one can destroy an
    active semaphore
  */
  this->unlock_writing();
  rwlock_destroy(&lock);
  DBUG_VOID_RETURN;
}


547 548
extern "C"
{
549
uchar *query_cache_query_get_key(const uchar *record, size_t *length,
550
				my_bool not_used)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
551
{
552
  Query_cache_block *query_block = (Query_cache_block*) record;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
553 554
  *length = (query_block->used - query_block->headers_len() -
	     ALIGN_SIZE(sizeof(Query_cache_query)));
555
  return (((uchar *) query_block->data()) +
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
556 557 558 559 560
	  ALIGN_SIZE(sizeof(Query_cache_query)));
}
}

/*****************************************************************************
561
  Functions to store things into the query cache
562
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
563

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
/*
  Note on double-check locking (DCL) usage.

  Below, in query_cache_insert(), query_cache_abort() and
  query_cache_end_of_result() we use what is called double-check
  locking (DCL) for NET::query_cache_query.  I.e. we test it first
  without a lock, and, if positive, test again under the lock.

  This means that if we see 'NET::query_cache_query == 0' without a
  lock we will skip the operation.  But this is safe here: when we
  started to cache a query, we called Query_cache::store_query(), and
  NET::query_cache_query was set to non-zero in this thread (and the
  thread always sees results of its memory operations, mutex or not).
  If later we see 'NET::query_cache_query == 0' without locking a
  mutex, that may only mean that some other thread have reset it by
  invalidating the query.  Skipping the operation in this case is the
  right thing to do, as NET::query_cache_query won't get non-zero for
  this query again.

  See also comments in Query_cache::store_query() and
  Query_cache::send_result_to_client().

  NOTE, however, that double-check locking is not applicable in
  'invalidate' functions, as we may erroneously skip invalidation,
  because the thread doing invalidation may never see non-zero
  NET::query_cache_query.
*/


void query_cache_init_query(NET *net)
{
  /*
    It is safe to initialize 'NET::query_cache_query' without a lock
    here, because before it will be accessed from different threads it
    will be set in this thread under a lock, and access from the same
    thread is always safe.
  */
  net->query_cache_query= 0;
}


605 606 607 608
/*
  Insert the packet into the query cache.
*/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
609 610 611 612
void query_cache_insert(NET *net, const char *packet, ulong length)
{
  DBUG_ENTER("query_cache_insert");

613 614 615 616
  /* See the comment on double-check locking usage above. */
  if (net->query_cache_query == 0)
    DBUG_VOID_RETURN;

617
  STRUCT_LOCK(&query_cache.structure_guard_mutex);
618 619 620

  if (unlikely(query_cache.query_cache_size == 0 ||
               query_cache.flush_in_progress))
621 622
  {
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
623
    DBUG_VOID_RETURN;
624
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
625

626 627 628
  Query_cache_block *query_block = ((Query_cache_block*)
				    net->query_cache_query);
  if (query_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
629
  {
630 631
    Query_cache_query *header = query_block->query();
    Query_cache_block *result = header->result();
632

633 634
    DUMP(&query_cache);
    BLOCK_LOCK_WR(query_block);
635
    DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
636

637 638 639 640 641
    /*
      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)
    */
642
    if (!query_cache.append_result_data(&result, length, (uchar*) packet,
643 644 645
					query_block))
    {
      DBUG_PRINT("warning", ("Can't append data"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
646
      header->result(result);
647 648 649 650
      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
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
651
      STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
652 653 654
      DBUG_VOID_RETURN;
    }
    header->result(result);
655
    header->last_pkt_nr= net->pkt_nr;
656
    BLOCK_UNLOCK_WR(query_block);
657
    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
658
  }
659 660
  else
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
661 662 663
  DBUG_VOID_RETURN;
}

664

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
665 666 667 668
void query_cache_abort(NET *net)
{
  DBUG_ENTER("query_cache_abort");

669 670 671 672 673 674 675 676
  /* See the comment on double-check locking usage above. */
  if (net->query_cache_query == 0)
    DBUG_VOID_RETURN;

  STRUCT_LOCK(&query_cache.structure_guard_mutex);

  if (unlikely(query_cache.query_cache_size == 0 ||
               query_cache.flush_in_progress))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
677
  {
678 679 680
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
    DBUG_VOID_RETURN;
  }
681

682 683 684 685 686 687 688 689 690
  Query_cache_block *query_block= ((Query_cache_block*)
                                   net->query_cache_query);
  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;
691
    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
692
  }
693 694 695

  STRUCT_UNLOCK(&query_cache.structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
696 697 698
  DBUG_VOID_RETURN;
}

699

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
700
void query_cache_end_of_result(THD *thd)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
701
{
702
  Query_cache_block *query_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
703 704
  DBUG_ENTER("query_cache_end_of_result");

705 706 707 708
  /* See the comment on double-check locking usage above. */
  if (thd->net.query_cache_query == 0)
    DBUG_VOID_RETURN;

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
709
#ifdef EMBEDDED_LIBRARY
710 711
  query_cache_insert(&thd->net, (char*)thd, 
                     emb_count_querycache_size(thd));
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
712
#endif
713

714 715 716 717
  STRUCT_LOCK(&query_cache.structure_guard_mutex);

  if (unlikely(query_cache.query_cache_size == 0 ||
               query_cache.flush_in_progress))
718
    goto end;
719

720
  query_block= ((Query_cache_block*) thd->net.query_cache_query);
721 722 723 724 725 726 727 728 729 730
  if (query_block)
  {
    DUMP(&query_cache);
    BLOCK_LOCK_WR(query_block);
    Query_cache_query *header= query_block->query();
    Query_cache_block *last_result_block= header->result()->prev;
    ulong allign_size= ALIGN_SIZE(last_result_block->used);
    ulong len= max(query_cache.min_allocation_unit, allign_size);
    if (last_result_block->length >= query_cache.min_allocation_unit + len)
      query_cache.split_block(last_result_block,len);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
731 732

#ifndef DBUG_OFF
733
    if (header->result() == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
734
    {
735 736 737 738
      DBUG_PRINT("error", ("end of data whith no result. query '%s'",
                           header->query()));
      query_cache.wreck(__LINE__, "");

739 740 741 742 743
      /*
        We do not need call of BLOCK_UNLOCK_WR(query_block); here because
        query_cache.wreck() switched query cache off but left content
        untouched for investigation (it is debugging method).
      */
744
      goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
745
    }
746 747 748 749 750
#endif
    header->found_rows(current_thd->limit_found_rows);
    header->result()->type= Query_cache_block::RESULT;
    header->writer(0);
    thd->net.query_cache_query= 0;
751
    BLOCK_UNLOCK_WR(query_block);
752 753
    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
754
  }
755

756 757
end:
  STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
758 759 760
  DBUG_VOID_RETURN;
}

761
void query_cache_invalidate_by_MyISAM_filename(const char *filename)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
762 763
{
  query_cache.invalidate_by_MyISAM_filename(filename);
764
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
765 766 767
}


768 769 770 771 772 773 774 775 776 777 778 779
/*
  The following function forms part of the C plugin API
*/
extern "C"
void mysql_query_cache_invalidate4(THD *thd,
                                   const char *key, unsigned key_length,
                                   int using_trx)
{
  query_cache.invalidate(thd, key, (uint32) key_length, (my_bool) using_trx);
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
780
/*****************************************************************************
781 782
   Query_cache methods
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
783

784 785 786 787 788
Query_cache::Query_cache(ulong query_cache_limit_arg,
			 ulong min_allocation_unit_arg,
			 ulong min_result_data_size_arg,
			 uint def_query_hash_size_arg,
			 uint def_table_hash_size_arg)
789
  :query_cache_size(0),
790
   query_cache_limit(query_cache_limit_arg),
791
   queries_in_cache(0), hits(0), inserts(0), refused(0),
792
   total_blocks(0), lowmem_prunes(0),
793 794 795 796
   min_allocation_unit(ALIGN_SIZE(min_allocation_unit_arg)),
   min_result_data_size(ALIGN_SIZE(min_result_data_size_arg)),
   def_query_hash_size(ALIGN_SIZE(def_query_hash_size_arg)),
   def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)),
797 798
   initialized(0)
{
799 800 801
  ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) +
		     ALIGN_SIZE(sizeof(Query_cache_block_table)) +
		     ALIGN_SIZE(sizeof(Query_cache_query)) + 3);
802
  set_if_bigger(min_allocation_unit,min_needed);
803
  this->min_allocation_unit= ALIGN_SIZE(min_allocation_unit);
804
  set_if_bigger(this->min_result_data_size,min_allocation_unit);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
805 806
}

807

808
ulong Query_cache::resize(ulong query_cache_size_arg)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
809 810
{
  DBUG_ENTER("Query_cache::resize");
811 812
  DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size,
			query_cache_size_arg));
813
  DBUG_ASSERT(initialized);
814

815
  STRUCT_LOCK(&structure_guard_mutex);
816 817 818 819 820
  while (flush_in_progress)
    pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
  flush_in_progress= TRUE;
  STRUCT_UNLOCK(&structure_guard_mutex);

821
  free_cache();
822

823
  query_cache_size= query_cache_size_arg;
824 825 826 827 828 829 830
  ulong new_query_cache_size= init_cache();

  DBUG_EXECUTE("check_querycache",check_integrity(0););

  STRUCT_LOCK(&structure_guard_mutex);
  flush_in_progress= FALSE;
  pthread_cond_signal(&COND_flush_finished);
831
  STRUCT_UNLOCK(&structure_guard_mutex);
832 833

  DBUG_RETURN(new_query_cache_size);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
834 835
}

836

837 838 839 840 841 842 843 844
ulong Query_cache::set_min_res_unit(ulong size)
{
  if (size < min_allocation_unit)
    size= min_allocation_unit;
  return (min_result_data_size= ALIGN_SIZE(size));
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
845 846
void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
{
847
  TABLE_COUNTER_TYPE local_tables;
848
  ulong tot_length;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
849
  DBUG_ENTER("Query_cache::store_query");
850 851 852 853 854 855 856 857 858
  /*
    Testing 'query_cache_size' without a lock here is safe: the thing
    we may loose is that the query won't be cached, but we save on
    mutex locking in the case when query cache is disabled or the
    query is uncachable.

    See also a note on double-check locking usage above.
  */
  if (thd->locked_tables || query_cache_size == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
859
    DBUG_VOID_RETURN;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
860
  uint8 tables_type= 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
861

862
  if ((local_tables= is_cacheable(thd, thd->query_length,
pem@mysql.com's avatar
pem@mysql.com committed
863
				  thd->query, thd->lex, tables_used,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
864
				  &tables_type)))
865
  {
866
    NET *net= &thd->net;
867 868 869
    Query_cache_query_flags flags;
    // fill all gaps between fields with 0 to get repeatable key
    bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
870 871 872
    flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
    flags.client_protocol_41= test(thd->client_capabilities &
                                   CLIENT_PROTOCOL_41);
873 874 875 876 877 878
    /*
      Protocol influences result format, so statement results in the binary
      protocol (COM_EXECUTE) cannot be served to statements asking for results
      in the text protocol (COM_QUERY) and vice-versa.
    */
    flags.result_in_binary_protocol= (unsigned int) thd->protocol->type();
879 880
    flags.more_results_exists= test(thd->server_status &
                                    SERVER_MORE_RESULTS_EXISTS);
881
    flags.pkt_nr= net->pkt_nr;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
882 883 884
    flags.character_set_client_num=
      thd->variables.character_set_client->number;
    flags.character_set_results_num=
885 886 887
      (thd->variables.character_set_results ?
       thd->variables.character_set_results->number :
       UINT_MAX);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
888 889
    flags.collation_connection_num=
      thd->variables.collation_connection->number;
890
    flags.limit= thd->variables.select_limit;
891
    flags.time_zone= thd->variables.time_zone;
892 893
    flags.sql_mode= thd->variables.sql_mode;
    flags.max_sort_length= thd->variables.max_sort_length;
894
    flags.lc_time_names= thd->variables.lc_time_names;
895
    flags.group_concat_max_len= thd->variables.group_concat_max_len;
896 897
    flags.div_precision_increment= thd->variables.div_precincrement;
    flags.default_week_format= thd->variables.default_week_format;
898 899
    DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
900
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
901 902
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",                          
903 904
                          (int)flags.client_long_flag,
                          (int)flags.client_protocol_41,
905
                          (int)flags.result_in_binary_protocol,
906
                          (int)flags.more_results_exists,
907
                          flags.pkt_nr,
908 909 910
                          flags.character_set_client_num,
                          flags.character_set_results_num,
                          flags.collation_connection_num,
911 912
                          (ulong) flags.limit,
                          (ulong) flags.time_zone,
913 914
                          flags.sql_mode,
                          flags.max_sort_length,
915 916 917
                          flags.group_concat_max_len,
                          flags.div_precision_increment,
                          flags.default_week_format));
918 919 920 921 922
    /*
     Make InnoDB to release the adaptive hash index latch before
     acquiring the query cache mutex.
    */
    ha_release_temporary_latches(thd);
923

924 925
    STRUCT_LOCK(&structure_guard_mutex);
    if (query_cache_size == 0 || flush_in_progress)
926 927
    {
      STRUCT_UNLOCK(&structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
928
      DBUG_VOID_RETURN;
929
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
930 931
    DUMP(this);

932 933
    if (ask_handler_allowance(thd, tables_used))
    {
934
      refused++;
935 936 937 938
      STRUCT_UNLOCK(&structure_guard_mutex);
      DBUG_VOID_RETURN;
    }

939 940 941
    /* Key is query + database + flag */
    if (thd->db_length)
    {
942
      memcpy(thd->query+thd->query_length+1, thd->db, thd->db_length);
943
      DBUG_PRINT("qcache", ("database: %s  length: %u",
944 945 946 947 948 949
			    thd->db, thd->db_length)); 
    }
    else
    {
      DBUG_PRINT("qcache", ("No active database"));
    }
950 951
    tot_length= thd->query_length + thd->db_length + 1 +
      QUERY_CACHE_FLAGS_SIZE;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
952
    /*
953 954
      We should only copy structure (don't use it location directly)
      because of alignment issue
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
955
    */
956 957
    memcpy((void *)(thd->query + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
	   &flags, QUERY_CACHE_FLAGS_SIZE);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
958

959
    /* Check if another thread is processing the same query? */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
960
    Query_cache_block *competitor = (Query_cache_block *)
961
      hash_search(&queries, (uchar*) thd->query, tot_length);
962
    DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
963 964
    if (competitor == 0)
    {
965 966
      /* Query is not in cache and no one is working with it; Store it */
      Query_cache_block *query_block;
967
      query_block= write_block_data(tot_length, (uchar*) thd->query,
968
				    ALIGN_SIZE(sizeof(Query_cache_query)),
969
				    Query_cache_block::QUERY, local_tables, 1);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
970 971
      if (query_block != 0)
      {
972 973
	DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
			    (ulong) query_block, query_block->used));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
974

975
	Query_cache_query *header = query_block->query();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
976
	header->init_n_lock();
977
	if (my_hash_insert(&queries, (uchar*) query_block))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
978 979
	{
	  refused++;
980
	  DBUG_PRINT("qcache", ("insertion in query hash"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
981 982 983
	  header->unlock_n_destroy();
	  free_memory_block(query_block);
	  STRUCT_UNLOCK(&structure_guard_mutex);
984
	  goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
985
	}
986
	if (!register_all_tables(query_block, tables_used, local_tables))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
987 988
	{
	  refused++;
989
	  DBUG_PRINT("warning", ("tables list including failed"));
990
	  hash_delete(&queries, (uchar *) query_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
991 992 993
	  header->unlock_n_destroy();
	  free_memory_block(query_block);
	  STRUCT_UNLOCK(&structure_guard_mutex);
994
	  goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
995
	}
996
	double_linked_list_simple_include(query_block, &queries_blocks);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
997 998
	inserts++;
	queries_in_cache++;
999
	net->query_cache_query= (uchar*) query_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1000
	header->writer(net);
1001
	header->tables_type(tables_type);
1002 1003 1004

	STRUCT_UNLOCK(&structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1005 1006 1007 1008 1009 1010
	// init_n_lock make query block locked
	BLOCK_UNLOCK_WR(query_block);
      }
      else
      {
	// We have not enough memory to store query => do nothing
1011
	refused++;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1012 1013 1014 1015 1016 1017
	STRUCT_UNLOCK(&structure_guard_mutex);
	DBUG_PRINT("warning", ("Can't allocate query"));
      }
    }
    else
    {
1018
      // Another thread is processing the same query => do nothing
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1019 1020
      refused++;
      STRUCT_UNLOCK(&structure_guard_mutex);
1021
      DBUG_PRINT("qcache", ("Another thread process same query"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1022 1023
    }
  }
monty@mysql.com's avatar
monty@mysql.com committed
1024
  else if (thd->lex->sql_command == SQLCOM_SELECT)
1025
    statistic_increment(refused, &structure_guard_mutex);
1026 1027

end:
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1028 1029
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1030

1031

1032 1033 1034 1035 1036 1037 1038 1039 1040
/*
  Check if the query is in the cache. If it was cached, send it
  to the user.

  RESULTS
        1	Query was not cached.
	0	The query was cached and user was sent the result.
	-1	The query was cached but we didn't have rights to use it.
		No error is sent to the client yet.
1041 1042 1043 1044

  NOTE
  This method requires that sql points to allocated memory of size:
  tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
1045
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1046

1047
int
1048 1049
Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
{
1050
  ulonglong engine_data;
1051 1052 1053
  Query_cache_query *query;
  Query_cache_block *first_result_block, *result_block;
  Query_cache_block_table *block_table, *block_table_end;
1054
  ulong tot_length;
1055
  Query_cache_query_flags flags;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1056 1057
  DBUG_ENTER("Query_cache::send_result_to_client");

1058 1059 1060 1061
  /*
    Testing 'query_cache_size' without a lock here is safe: the thing
    we may loose is that the query won't be served from cache, but we
    save on mutex locking in the case when query cache is disabled.
1062

1063 1064 1065 1066 1067
    See also a note on double-check locking usage above.
  */
  if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
      query_cache_size == 0)
    goto err;
1068

1069
  if (!thd->lex->safe_to_cache_query)
1070
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1071
    DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
1072
    goto err;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1073 1074 1075
  }

  {
1076 1077 1078 1079 1080 1081 1082
    uint i= 0;
    /*
      Skip '(' characters in queries like following:
      (select a from t1) union (select a from t1);
    */
    while (sql[i]=='(')
      i++;
1083

1084 1085
    /*
      Test if the query is a SELECT
evgen@moonbone.local's avatar
evgen@moonbone.local committed
1086 1087 1088
      (pre-space is removed in dispatch_command).

      First '/' looks like comment before command it is not
1089
      frequently appeared in real life, consequently we can
evgen@moonbone.local's avatar
evgen@moonbone.local committed
1090
      check all such queries, too.
1091
    */
evgen@moonbone.local's avatar
evgen@moonbone.local committed
1092
    if ((my_toupper(system_charset_info, sql[i])     != 'S' ||
1093 1094
         my_toupper(system_charset_info, sql[i + 1]) != 'E' ||
         my_toupper(system_charset_info, sql[i + 2]) != 'L') &&
1095
        sql[i] != '/')
1096 1097 1098 1099
    {
      DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
      goto err;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1100 1101 1102
  }

  STRUCT_LOCK(&structure_guard_mutex);
1103
  if (query_cache_size == 0 || flush_in_progress)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1104
  {
1105 1106
    DBUG_PRINT("qcache", ("query cache disabled"));
    goto err_unlock;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1107
  }
1108 1109 1110 1111

  /* Check that we haven't forgot to reset the query cache variables */
  DBUG_ASSERT(thd->net.query_cache_query == 0);

1112
  Query_cache_block *query_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1113

1114
  tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
1115 1116
  if (thd->db_length)
  {
1117
    memcpy(sql+query_length+1, thd->db, thd->db_length);
1118
    DBUG_PRINT("qcache", ("database: '%s'  length: %u",
1119 1120 1121 1122 1123 1124
			  thd->db, thd->db_length));
  }
  else
  {
    DBUG_PRINT("qcache", ("No active database"));
  }
1125 1126 1127

  // fill all gaps between fields with 0 to get repeatable key
  bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
1128 1129 1130
  flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
  flags.client_protocol_41= test(thd->client_capabilities &
                                 CLIENT_PROTOCOL_41);
1131
  flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
1132 1133
  flags.more_results_exists= test(thd->server_status &
                                  SERVER_MORE_RESULTS_EXISTS);
1134
  flags.pkt_nr= thd->net.pkt_nr;
1135
  flags.character_set_client_num= thd->variables.character_set_client->number;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1136
  flags.character_set_results_num=
1137 1138 1139
    (thd->variables.character_set_results ?
     thd->variables.character_set_results->number :
     UINT_MAX);
1140
  flags.collation_connection_num= thd->variables.collation_connection->number;
1141
  flags.limit= thd->variables.select_limit;
1142
  flags.time_zone= thd->variables.time_zone;
1143 1144 1145
  flags.sql_mode= thd->variables.sql_mode;
  flags.max_sort_length= thd->variables.max_sort_length;
  flags.group_concat_max_len= thd->variables.group_concat_max_len;
1146 1147
  flags.div_precision_increment= thd->variables.div_precincrement;
  flags.default_week_format= thd->variables.default_week_format;
1148
  flags.lc_time_names= thd->variables.lc_time_names;
1149 1150
  DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
1151
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
1152 1153
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",                          
1154 1155
                          (int)flags.client_long_flag,
                          (int)flags.client_protocol_41,
1156
                          (int)flags.result_in_binary_protocol,
1157
                          (int)flags.more_results_exists,
1158
                          flags.pkt_nr,
1159 1160 1161
                          flags.character_set_client_num,
                          flags.character_set_results_num,
                          flags.collation_connection_num,
1162 1163
                          (ulong) flags.limit,
                          (ulong) flags.time_zone,
1164 1165
                          flags.sql_mode,
                          flags.max_sort_length,
1166 1167 1168
                          flags.group_concat_max_len,
                          flags.div_precision_increment,
                          flags.default_week_format));
1169 1170 1171
  memcpy((uchar *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
	 (uchar*) &flags, QUERY_CACHE_FLAGS_SIZE);
  query_block = (Query_cache_block *)  hash_search(&queries, (uchar*) sql,
1172
						   tot_length);
1173
  /* Quick abort on unlocked data */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1174 1175 1176 1177
  if (query_block == 0 ||
      query_block->query()->result() == 0 ||
      query_block->query()->result()->type != Query_cache_block::RESULT)
  {
1178
    DBUG_PRINT("qcache", ("No query in query hash or no results"));
1179
    goto err_unlock;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1180
  }
1181
  DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1182

1183
  /* Now lock and test that nothing changed while blocks was unlocked */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1184 1185
  BLOCK_LOCK_RD(query_block);

1186 1187 1188 1189 1190 1191 1192
  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"));
1193
    BLOCK_UNLOCK_RD(query_block);
1194
    goto err_unlock;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1195
  }
1196 1197
  DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query));

1198 1199 1200 1201 1202 1203 1204 1205 1206
  if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
      (query->tables_type() & HA_CACHE_TBL_TRANSACT))
  {
    DBUG_PRINT("qcache",
	       ("we are in transaction and have transaction tables in query"));
    BLOCK_UNLOCK_RD(query_block);
    goto err_unlock;
  }
      
1207 1208 1209
  // Check access;
  block_table= query_block->table(0);
  block_table_end= block_table+query_block->n_tables;
1210
  for (; block_table != block_table_end; block_table++)
1211 1212
  {
    TABLE_LIST table_list;
1213
    TABLE *tmptable;
1214
    Query_cache_table *table = block_table->parent;
1215 1216 1217 1218 1219 1220 1221 1222 1223

    /*
      Check that we have not temporary tables with same names of tables
      of this query. If we have such tables, we will not send data from
      query cache, because temporary tables hide real tables by which
      query in query cache was made.
    */
    for (tmptable= thd->temporary_tables; tmptable ; tmptable= tmptable->next)
    {
1224
      if (tmptable->s->table_cache_key.length - TMP_TABLE_KEY_EXTRA == 
1225
          table->key_length() &&
1226
          !memcmp(tmptable->s->table_cache_key.str, table->data(),
1227
                  table->key_length()))
1228 1229 1230 1231 1232 1233 1234
      {
        DBUG_PRINT("qcache",
                   ("Temporary table detected: '%s.%s'",
                    table_list.db, table_list.alias));
        STRUCT_UNLOCK(&structure_guard_mutex);
        /*
          We should not store result of this query because it contain
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1235
          temporary tables => assign following variable to make check
1236 1237
          faster.
        */
1238
        thd->lex->safe_to_cache_query=0;
1239 1240 1241 1242 1243 1244
        BLOCK_UNLOCK_RD(query_block);
        DBUG_RETURN(-1);
      }
    }

    bzero((char*) &table_list,sizeof(table_list));
1245
    table_list.db = table->db();
1246
    table_list.alias= table_list.table_name= table->table();
hf@deer.(none)'s avatar
hf@deer.(none) committed
1247
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1248
    if (check_table_access(thd,SELECT_ACL,&table_list,1))
1249 1250 1251
    {
      DBUG_PRINT("qcache",
		 ("probably no SELECT access to %s.%s =>  return to normal processing",
1252
		  table_list.db, table_list.alias));
1253
      STRUCT_UNLOCK(&structure_guard_mutex);
1254
      thd->lex->safe_to_cache_query=0;		// Don't try to cache this
1255 1256 1257 1258 1259 1260
      BLOCK_UNLOCK_RD(query_block);
      DBUG_RETURN(-1);				// Privilege error
    }
    if (table_list.grant.want_privilege)
    {
      DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
1261
			    table_list.db, table_list.alias));
1262
      BLOCK_UNLOCK_RD(query_block);
1263
      thd->lex->safe_to_cache_query= 0;		// Don't try to cache this
1264
      goto err_unlock;				// Parse query
1265
    }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1266
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
1267 1268 1269 1270 1271
    engine_data= table->engine_data();
    if (table->callback() &&
        !(*table->callback())(thd, table->db(),
                              table->key_length(),
                              &engine_data))
1272
    {
1273
      DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
1274 1275
			    table_list.db, table_list.alias));
      BLOCK_UNLOCK_RD(query_block);
1276 1277 1278
      if (engine_data != table->engine_data())
      {
        DBUG_PRINT("qcache",
1279 1280 1281
                   ("Handler require invalidation queries of %s.%s %lu-%lu",
                    table_list.db, table_list.alias,
                    (ulong) engine_data, (ulong) table->engine_data()));
1282
        invalidate_table((uchar *) table->db(), table->key_length());
1283
      }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1284 1285
      else
        thd->lex->safe_to_cache_query= 0;       // Don't try to cache this
1286
      goto err_unlock;				// Parse query
1287
    }
1288
    else
1289 1290
      DBUG_PRINT("qcache", ("handler allow caching %s,%s",
			    table_list.db, table_list.alias));
1291
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1292 1293
  move_to_query_list_end(query_block);
  hits++;
1294 1295
  STRUCT_UNLOCK(&structure_guard_mutex);

1296 1297 1298
  /*
    Send cached result to client
  */
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1299
#ifndef EMBEDDED_LIBRARY
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1300 1301
  do
  {
1302
    DBUG_PRINT("qcache", ("Results  (len: %lu  used: %lu  headers: %lu)",
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1303
			  result_block->length, result_block->used,
1304 1305
			  (ulong) (result_block->headers_len()+
                                   ALIGN_SIZE(sizeof(Query_cache_result)))));
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1306
    
1307 1308 1309 1310 1311 1312
    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
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1313
    result_block = result_block->next;
1314
    thd->net.pkt_nr= query->last_pkt_nr; // Keep packet number updated
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1315
  } while (result_block != first_result_block);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1316 1317 1318 1319 1320 1321 1322
#else
  {
    Querycache_stream qs(result_block, result_block->headers_len() +
			 ALIGN_SIZE(sizeof(Query_cache_result)));
    emb_load_querycache_result(thd, &qs);
  }
#endif /*!EMBEDDED_LIBRARY*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1323 1324

  thd->limit_found_rows = query->found_rows();
1325
  thd->status_var.last_query_cost= 0.0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1326 1327

  BLOCK_UNLOCK_RD(query_block);
1328
  DBUG_RETURN(1);				// Result sent to client
1329

1330 1331
err_unlock:
  STRUCT_UNLOCK(&structure_guard_mutex);
1332
err:
1333
  DBUG_RETURN(0);				// Query was not cached
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1334 1335
}

1336

1337 1338 1339 1340
/*
  Remove all cached queries that uses any of the tables in the list
*/

1341 1342
void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
			     my_bool using_transactions)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1343 1344
{
  DBUG_ENTER("Query_cache::invalidate (table list)");
1345 1346
  STRUCT_LOCK(&structure_guard_mutex);
  if (query_cache_size > 0 && !flush_in_progress)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1347
  {
1348
    DUMP(this);
1349

1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
    using_transactions= using_transactions &&
      (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
    for (; tables_used; tables_used= tables_used->next_local)
    {
      DBUG_ASSERT(!using_transactions || tables_used->table!=0);
      if (tables_used->derived)
        continue;
      if (using_transactions &&
         (tables_used->table->file->table_cache_type() ==
          HA_CACHE_TBL_TRANSACT))
        /*
           Tables_used->table can't be 0 in transaction.
           Only 'drop' invalidate not opened table, but 'drop'
           force transaction finish.
        */
        thd->add_changed_table(tables_used->table);
      else
        invalidate_table(tables_used);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1368 1369
    }
  }
1370 1371
  STRUCT_UNLOCK(&structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1372 1373 1374
  DBUG_VOID_RETURN;
}

1375
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1376
{
1377
  DBUG_ENTER("Query_cache::invalidate (changed table list)");
1378
  if (tables_used)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1379 1380
  {
    STRUCT_LOCK(&structure_guard_mutex);
1381
    if (query_cache_size > 0 && !flush_in_progress)
1382 1383
    {
      DUMP(this);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1384
      for (; tables_used; tables_used= tables_used->next)
1385
      {
1386
	invalidate_table((uchar*) tables_used->key, tables_used->key_length);
1387
	DBUG_PRINT("qcache", ("db: %s  table: %s", tables_used->key,
1388 1389
			      tables_used->key+
			      strlen(tables_used->key)+1));
1390 1391
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1392 1393 1394
    STRUCT_UNLOCK(&structure_guard_mutex);
  }
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1395 1396
}

1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409

/*
  Invalidate locked for write

  SYNOPSIS
    Query_cache::invalidate_locked_for_write()
    tables_used - table list

  NOTE
    can be used only for opened tables
*/
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
1410
  DBUG_ENTER("Query_cache::invalidate_locked_for_write");
1411
  if (tables_used)
1412 1413
  {
    STRUCT_LOCK(&structure_guard_mutex);
1414
    if (query_cache_size > 0 && !flush_in_progress)
1415 1416
    {
      DUMP(this);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1417
      for (; tables_used; tables_used= tables_used->next_local)
1418
      {
1419 1420
        if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
            tables_used->table)
1421 1422 1423 1424 1425 1426 1427 1428
	  invalidate_table(tables_used->table);
      }
    }
    STRUCT_UNLOCK(&structure_guard_mutex);
  }
  DBUG_VOID_RETURN;
}

1429
/*
1430
  Remove all cached queries that uses the given table
1431 1432
*/

1433 1434
void Query_cache::invalidate(THD *thd, TABLE *table, 
			     my_bool using_transactions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1435
{
1436 1437
  DBUG_ENTER("Query_cache::invalidate (table)");
  
1438 1439
  STRUCT_LOCK(&structure_guard_mutex);
  if (query_cache_size > 0 && !flush_in_progress)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1440
  {
1441 1442 1443 1444 1445 1446 1447
    using_transactions= using_transactions &&
      (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
    if (using_transactions && 
        (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
      thd->add_changed_table(table);
    else
      invalidate_table(table);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1448
  }
1449 1450
  STRUCT_UNLOCK(&structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1451
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1452 1453
}

1454 1455 1456 1457 1458
void Query_cache::invalidate(THD *thd, const char *key, uint32  key_length,
			     my_bool using_transactions)
{
  DBUG_ENTER("Query_cache::invalidate (key)");
  
1459 1460
  STRUCT_LOCK(&structure_guard_mutex);
  if (query_cache_size > 0 && !flush_in_progress)
1461
  {
1462
    using_transactions= using_transactions &&
1463 1464 1465 1466
      (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
    if (using_transactions) // used for innodb => has_transactions() is TRUE
      thd->add_changed_table(key, key_length);
    else
1467
      invalidate_table((uchar*)key, key_length);
1468
  }
1469 1470
  STRUCT_UNLOCK(&structure_guard_mutex);  

1471 1472 1473
  DBUG_VOID_RETURN;
}

1474 1475
/**
   @brief Remove all cached queries that uses the given database
1476
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1477
void Query_cache::invalidate(char *db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1478
{
1479
  bool restart= FALSE;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1480
  DBUG_ENTER("Query_cache::invalidate (db)");
1481 1482
  STRUCT_LOCK(&structure_guard_mutex);
  if (query_cache_size > 0 && !flush_in_progress)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1483
  {
1484 1485
    if (tables_blocks)
    {
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520
      Query_cache_block *table_block = tables_blocks;
      do {
        restart= FALSE;
        do
        {
          Query_cache_block *next= table_block->next;
          Query_cache_table *table = table_block->table();
          if (strcmp(table->db(),db) == 0)
            invalidate_table(table_block);

          table_block= next;

          /*
            If our root node to used tables became null then the last element
            in the table list was removed when a query was invalidated;
            Terminate the search.
          */
          if (tables_blocks == 0)
          {
            table_block= tables_blocks;
          }
          /*
            If the iterated list has changed underlying structure;
            we need to restart the search.
          */
          else if (table_block->type == Query_cache_block::FREE)
          {
            restart= TRUE;
            table_block= tables_blocks;
          }
          /* 
            The used tables are linked in a circular list;
            loop until we return to the begining.
          */
        } while (table_block != tables_blocks);
1521
        /*
1522 1523 1524 1525 1526
           Invalidating a table will also mean that all cached queries using
           this table also will be invalidated. This will in turn change the
           list of tables associated with these queries and the linked list of
           used table will be changed. Because of this we might need to restart
           the search when a table has been invalidated.
1527
        */
1528 1529
      } while (restart);
    } // end if( tables_blocks )
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1530
  }
1531 1532
  STRUCT_UNLOCK(&structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1533
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1534 1535
}

1536 1537

void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1538 1539
{
  DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
1540 1541 1542

  STRUCT_LOCK(&structure_guard_mutex);
  if (query_cache_size > 0 && !flush_in_progress)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1543
  {
1544 1545
    /* Calculate the key outside the lock to make the lock shorter */
    char key[MAX_DBKEY_LENGTH];
1546 1547
    uint32 db_length;
    uint key_length= filename_2_table_key(key, filename, &db_length);
1548 1549
    Query_cache_block *table_block;
    if ((table_block = (Query_cache_block*) hash_search(&tables,
1550 1551
                                                        (uchar*) key,
                                                        key_length)))
1552
      invalidate_table(table_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1553
  }
1554 1555
  STRUCT_UNLOCK(&structure_guard_mutex);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1556 1557
  DBUG_VOID_RETURN;
}
1558

1559
  /* Remove all queries from cache */
1560

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1561
void Query_cache::flush()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1562
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1563
  DBUG_ENTER("Query_cache::flush");
1564
  STRUCT_LOCK(&structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1565 1566 1567 1568 1569 1570
  if (query_cache_size > 0)
  {
    DUMP(this);
    flush_cache();
    DUMP(this);
  }
1571 1572

  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
1573
  STRUCT_UNLOCK(&structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1574
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1575 1576
}

1577 1578
  /* Join result in cache in 1 block (if result length > join_limit) */

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
  DBUG_ENTER("Query_cache::pack");
  uint i = 0;
  do
  {
    pack_cache();
  } while ((++i < iteration_limit) && join_results(join_limit));
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1589

1590

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1591
void Query_cache::destroy()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1592
{
1593 1594
  DBUG_ENTER("Query_cache::destroy");
  if (!initialized)
1595 1596 1597
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
  }
1598 1599
  else
  {
1600 1601
    /* Underlying code expects the lock. */
    STRUCT_LOCK(&structure_guard_mutex);
1602
    free_cache();
1603 1604 1605
    STRUCT_UNLOCK(&structure_guard_mutex);

    pthread_cond_destroy(&COND_flush_finished);
1606 1607 1608
    pthread_mutex_destroy(&structure_guard_mutex);
    initialized = 0;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1609 1610
  DBUG_VOID_RETURN;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1611

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1612 1613

/*****************************************************************************
1614 1615
  init/destroy
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1616 1617 1618 1619 1620

void Query_cache::init()
{
  DBUG_ENTER("Query_cache::init");
  pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
1621 1622
  pthread_cond_init(&COND_flush_finished, NULL);
  flush_in_progress= FALSE;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1623 1624 1625 1626
  initialized = 1;
  DBUG_VOID_RETURN;
}

1627

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1628 1629
ulong Query_cache::init_cache()
{
1630 1631 1632
  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;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1633
  int align;
1634

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1635
  DBUG_ENTER("Query_cache::init_cache");
1636

1637
  approx_additional_data_size = (sizeof(Query_cache) +
1638
				 sizeof(uchar*)*(def_query_hash_size+
1639
					       def_table_hash_size));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1640
  if (query_cache_size < approx_additional_data_size)
1641
    goto err;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1642

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1643 1644
  query_cache_size-= approx_additional_data_size;
  align= query_cache_size % ALIGN_SIZE(1);
1645 1646 1647 1648 1649
  if (align)
  {
    query_cache_size-= align;
    approx_additional_data_size+= align;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1650

1651 1652 1653 1654
  /*
    Count memory bins number.
    Check section 6. in start comment for the used algorithm.
  */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1655

1656 1657 1658 1659 1660 1661 1662
  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;
1663 1664 1665 1666 1667 1668
  if (mem_bin_size <= min_allocation_unit)
  {
    DBUG_PRINT("qcache", ("too small query cache => query cache disabled"));
    // TODO here (and above) should be warning in 4.1
    goto err;
  }
1669 1670 1671 1672 1673
  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;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1674
    mem_bin_steps++;
1675 1676 1677 1678 1679 1680
    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);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1681
  }
1682 1683 1684 1685 1686 1687 1688 1689
  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))));

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1690
  if (query_cache_size < additional_data_size)
1691 1692
    goto err;
  query_cache_size -= additional_data_size;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1693

1694
  if (!(cache= (uchar *)
1695
        my_malloc_lock(query_cache_size+additional_data_size, MYF(0))))
1696
    goto err;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1697

1698 1699
  DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins",
		      query_cache_size, min_allocation_unit, mem_bin_num));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1700

1701 1702 1703 1704
  steps = (Query_cache_memory_bin_step *) cache;
  bins = ((Query_cache_memory_bin *)
	  (cache + mem_bin_steps *
	   ALIGN_SIZE(sizeof(Query_cache_memory_bin_step))));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1705

1706 1707
  first_block = (Query_cache_block *) (cache + additional_data_size);
  first_block->init(query_cache_size);
1708
  total_blocks++;
1709 1710
  first_block->pnext=first_block->pprev=first_block;
  first_block->next=first_block->prev=first_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1711

1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727
  /* 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;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1728
    }
1729 1730 1731 1732 1733 1734 1735 1736
    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);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1737
  }
1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755
  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;
    }
  }
1756 1757
  bins[mem_bin_num].number = 1;	// For easy end test in get_free_block
  free_memory = free_memory_blocks = 0;
1758 1759 1760 1761
  insert_into_free_memory_list(first_block);

  DUMP(this);

1762
  VOID(hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0,
1763
		 query_cache_query_get_key, 0, 0));
1764
#ifndef FN_NO_CASE_SENCE
1765 1766 1767 1768 1769 1770 1771 1772
  /*
    If lower_case_table_names!=0 then db and table names are already 
    converted to lower case and we can use binary collation for their 
    comparison (no matter if file system case sensitive or not).
    If we have case-sensitive file system (like on most Unixes) and
    lower_case_table_names == 0 then we should distinguish my_table
    and MY_TABLE cases and so again can use binary collation.
  */
1773
  VOID(hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0,
1774
		 query_cache_table_get_key, 0, 0));
1775
#else
1776
  /*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1777 1778 1779 1780 1781
    On windows, OS/2, MacOS X with HFS+ or any other case insensitive
    file system if lower_case_table_names!=0 we have same situation as
    in previous case, but if lower_case_table_names==0 then we should
    not distinguish cases (to be compatible in behavior with underlying
    file system) and so should use case insensitive collation for
1782 1783
    comparison.
  */
bar@bar.mysql.r18.ru's avatar
bar@bar.mysql.r18.ru committed
1784
  VOID(hash_init(&tables,
1785
		 lower_case_table_names ? &my_charset_bin :
1786
		 files_charset_info,
bar@bar.mysql.r18.ru's avatar
bar@bar.mysql.r18.ru committed
1787
		 def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0));
1788
#endif
1789

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1790 1791 1792 1793
  queries_in_cache = 0;
  queries_blocks = 0;
  DBUG_RETURN(query_cache_size +
	      additional_data_size + approx_additional_data_size);
1794 1795 1796 1797

err:
  make_disabled();
  DBUG_RETURN(0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1798 1799
}

1800 1801 1802

/* Disable the use of the query cache */

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1803 1804 1805
void Query_cache::make_disabled()
{
  DBUG_ENTER("Query_cache::make_disabled");
1806
  query_cache_size= 0;
1807
  queries_blocks= 0;
1808 1809 1810 1811 1812 1813 1814
  free_memory= 0;
  bins= 0;
  steps= 0;
  cache= 0;
  mem_bin_num= mem_bin_steps= 0;
  queries_in_cache= 0;
  first_block= 0;
1815 1816
  total_blocks= 0;
  tables_blocks= 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1817 1818 1819
  DBUG_VOID_RETURN;
}

1820

1821 1822 1823 1824 1825 1826
/**
  @class Query_cache
  @brief Free all resources allocated by the cache.
  @details  This function frees all resources allocated by the cache.  You
    have to call init_cache() before using the cache again. This function requires
    the structure_guard_mutex to be locked.
1827 1828
*/

1829
void Query_cache::free_cache()
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1830 1831 1832
{
  DBUG_ENTER("Query_cache::free_cache");

1833
  my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR));
1834 1835 1836
  make_disabled();
  hash_free(&queries);
  hash_free(&tables);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1837 1838 1839 1840
  DBUG_VOID_RETURN;
}

/*****************************************************************************
1841
  Free block data
1842 1843
*****************************************************************************/

1844

1845
/*
1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856
  flush_cache() - flush the cache.

  SYNOPSIS
    flush_cache()

  DESCRIPTION
    This function will flush cache contents.  It assumes we have
    'structure_guard_mutex' locked.  The function sets the
    flush_in_progress flag and releases the lock, so other threads may
    proceed skipping the cache as if it is disabled.  Concurrent
    flushes are performed in turn.
1857 1858 1859 1860 1861 1862

    After flush_cache() call, the cache is flushed, all the freed
    memory is accumulated in bin[0], and the 'structure_guard_mutex'
    is locked.  However, since we could release the mutex during
    execution, the rest of the cache state could have been changed,
    and should not be relied on.
1863
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1864 1865 1866

void Query_cache::flush_cache()
{
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885
  /*
    If there is flush in progress, wait for it to finish, and then do
    our flush.  This is necessary because something could be added to
    the cache before we acquire the lock again, and some code (like
    Query_cache::free_cache()) depends on the fact that after the
    flush the cache is empty.
  */
  while (flush_in_progress)
    pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);

  /*
    Setting 'flush_in_progress' will prevent other threads from using
    the cache while we are in the middle of the flush, and we release
    the lock so that other threads won't block.
  */
  flush_in_progress= TRUE;
  STRUCT_UNLOCK(&structure_guard_mutex);

  my_hash_reset(&queries);
1886 1887
  while (queries_blocks != 0)
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1888
    BLOCK_LOCK_WR(queries_blocks);
1889
    free_query_internal(queries_blocks);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1890
  }
1891 1892 1893 1894

  STRUCT_LOCK(&structure_guard_mutex);
  flush_in_progress= FALSE;
  pthread_cond_signal(&COND_flush_finished);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1895 1896
}

1897 1898 1899 1900 1901
/*
  Free oldest query that is not in use by another thread.
  Returns 1 if we couldn't remove anything
*/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1902 1903 1904
my_bool Query_cache::free_old_query()
{
  DBUG_ENTER("Query_cache::free_old_query");
1905
  if (queries_blocks)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1906
  {
1907 1908 1909 1910 1911 1912 1913
    /*
      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)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1914
    {
1915 1916 1917
      Query_cache_block *block = queries_blocks;
      /* Search until we find first query that we can remove */
      do
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1918
      {
1919 1920 1921 1922 1923 1924 1925 1926 1927 1928
	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 );
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1929

1930 1931 1932
    if (query_block != 0)
    {
      free_query(query_block);
1933
      lowmem_prunes++;
1934 1935
      DBUG_RETURN(0);
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1936
  }
1937
  DBUG_RETURN(1);				// Nothing to remove
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1938 1939
}

1940

1941
/*
1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956
  free_query_internal() - free query from query cache.

  SYNOPSIS
    free_query_internal()
      query_block           Query_cache_block representing the query

  DESCRIPTION
    This function will remove the query from a cache, and place its
    memory blocks to the list of free blocks.  'query_block' must be
    locked for writing, this function will release (and destroy) this
    lock.

  NOTE
    'query_block' should be removed from 'queries' hash _before_
    calling this method, as the lock will be destroyed here.
1957 1958
*/

1959
void Query_cache::free_query_internal(Query_cache_block *query_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1960
{
1961
  DBUG_ENTER("Query_cache::free_query_internal");
1962 1963
  DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
		      (ulong) query_block,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1964
		      query_block->query()->length() ));
1965

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1966 1967
  queries_in_cache--;

1968
  Query_cache_query *query= query_block->query();
1969

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1970 1971
  if (query->writer() != 0)
  {
1972
    /* Tell MySQL that this query should not be cached anymore */
1973
    query->writer()->query_cache_query= 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1974 1975
    query->writer(0);
  }
1976
  double_linked_list_exclude(query_block, &queries_blocks);
1977
  Query_cache_block_table *table= query_block->table(0);
1978

1979
  for (TABLE_COUNTER_TYPE i= 0; i < query_block->n_tables; i++)
1980
    unlink_table(table++);
1981
  Query_cache_block *result_block= query->result();
1982 1983 1984 1985 1986

  /*
    The following is true when query destruction was called and no results
    in query . (query just registered and then abort/pack/flush called)
  */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1987 1988
  if (result_block != 0)
  {
1989 1990 1991 1992 1993 1994
    if (result_block->type != Query_cache_block::RESULT)
    {
      // removing unfinished query
      refused++;
      inserts--;
    }
1995
    Query_cache_block *block= result_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1996 1997
    do
    {
1998 1999
      Query_cache_block *current= block;
      block= block->next;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2000 2001 2002
      free_memory_block(current);
    } while (block != result_block);
  }
2003 2004 2005 2006 2007 2008
  else
  {
    // removing unfinished query
    refused++;
    inserts--;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2009 2010 2011 2012 2013 2014 2015

  query->unlock_n_destroy();
  free_memory_block(query_block);

  DBUG_VOID_RETURN;
}

2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035

/*
  free_query() - free query from query cache.

  SYNOPSIS
    free_query()
      query_block           Query_cache_block representing the query

  DESCRIPTION
    This function will remove 'query_block' from 'queries' hash, and
    then call free_query_internal(), which see.
*/

void Query_cache::free_query(Query_cache_block *query_block)
{
  DBUG_ENTER("Query_cache::free_query");
  DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
		      (ulong) query_block,
		      query_block->query()->length() ));

2036
  hash_delete(&queries,(uchar *) query_block);
2037 2038 2039 2040 2041
  free_query_internal(query_block);

  DBUG_VOID_RETURN;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2042
/*****************************************************************************
2043 2044
 Query data creation
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2045 2046

Query_cache_block *
2047
Query_cache::write_block_data(ulong data_len, uchar* data,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2048 2049 2050 2051 2052
			      ulong header_len,
			      Query_cache_block::block_type type,
			      TABLE_COUNTER_TYPE ntab,
			      my_bool under_guard)
{
2053 2054 2055 2056
  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;
2057
  ulong align_len= ALIGN_SIZE(len);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2058
  DBUG_ENTER("Query_cache::write_block_data");
2059
  DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2060
		      data_len, header_len, all_headers_len));
2061 2062
  Query_cache_block *block = allocate_block(max(align_len, 
						min_allocation_unit),
2063
					    1, 0, under_guard);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2064 2065 2066 2067 2068 2069
  if (block != 0)
  {
    block->type = type;
    block->n_tables = ntab;
    block->used = len;

2070
    memcpy((uchar *) block+ all_headers_len, data, data_len);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2071 2072 2073 2074
  }
  DBUG_RETURN(block);
}

2075 2076 2077 2078 2079

/*
  On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be done.
*/

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2080
my_bool
2081
Query_cache::append_result_data(Query_cache_block **current_block,
2082
				ulong data_len, uchar* data,
2083
				Query_cache_block *query_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2084
{
2085 2086
  DBUG_ENTER("Query_cache::append_result_data");
  DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query",
2087
		      data_len, (long) query_block));
2088

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2089 2090
  if (query_block->query()->add(data_len) > query_cache_limit)
  {
2091
    DBUG_PRINT("qcache", ("size limit reached %lu > %lu",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2092 2093 2094 2095
			query_block->query()->length(),
			query_cache_limit));
    DBUG_RETURN(0);
  }
2096
  if (*current_block == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2097
  {
2098
    DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2099
    /*
2100 2101
      STRUCT_UNLOCK(&structure_guard_mutex) Will be done by
      write_result_data if success;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2102
    */
2103
    DBUG_RETURN(write_result_data(current_block, data_len, data, query_block,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2104 2105
				  Query_cache_block::RES_BEG));
  }
2106
  Query_cache_block *last_block = (*current_block)->prev;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2107

2108 2109
  DBUG_PRINT("qcache", ("lastblock 0x%lx len %lu used %lu",
		      (ulong) last_block, last_block->length,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2110 2111
		      last_block->used));
  my_bool success = 1;
2112
  ulong last_block_free_space= last_block->length - last_block->used;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2113

2114 2115 2116 2117 2118
  /*
    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.
  */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2119

2120
  // Try join blocks if physically next block is free...
2121 2122
  ulong tail = data_len - last_block_free_space;
  ulong append_min = get_min_append_result_data_size();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2123 2124
  if (last_block_free_space < data_len &&
      append_next_free_block(last_block,
2125
			     max(tail, append_min)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2126
    last_block_free_space = last_block->length - last_block->used;
2127
  // If no space in last block (even after join) allocate new block
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2128 2129
  if (last_block_free_space < data_len)
  {
2130
    DBUG_PRINT("qcache", ("allocate new block for %lu bytes",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2131 2132 2133
			data_len-last_block_free_space));
    Query_cache_block *new_block = 0;
    /*
2134 2135
      On success STRUCT_UNLOCK(&structure_guard_mutex) will be done
      by the next call
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2136
    */
2137
    success = write_result_data(&new_block, data_len-last_block_free_space,
2138
				(uchar*)(((uchar*)data)+last_block_free_space),
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2139 2140 2141
				query_block,
				Query_cache_block::RES_CONT);
    /*
2142 2143
       new_block may be != 0 even !success (if write_result_data
       allocate a small block but failed to allocate continue)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2144 2145 2146 2147 2148
    */
    if (new_block != 0)
      double_linked_list_join(last_block, new_block);
  }
  else
2149 2150
  {
    // It is success (nobody can prevent us write data)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2151
    STRUCT_UNLOCK(&structure_guard_mutex);
2152
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2153

2154 2155
  // Now finally write data to the last block
  if (success && last_block_free_space > 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2156 2157
  {
    ulong to_copy = min(data_len,last_block_free_space);
2158
    DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2159
			last_block_free_space, (ulong)last_block, to_copy));
2160
    memcpy((uchar*) last_block + last_block->used, data, to_copy);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2161 2162 2163 2164 2165
    last_block->used+=to_copy;
  }
  DBUG_RETURN(success);
}

2166 2167

my_bool Query_cache::write_result_data(Query_cache_block **result_block,
2168
				       ulong data_len, uchar* data,
2169
				       Query_cache_block *query_block,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2170 2171 2172
				       Query_cache_block::block_type type)
{
  DBUG_ENTER("Query_cache::write_result_data");
2173
  DBUG_PRINT("qcache", ("data_len %lu",data_len));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2174

2175 2176 2177 2178 2179 2180 2181 2182 2183
  /*
    Reserve block(s) for filling
    During data allocation we must have structure_guard_mutex locked.
    As data copy is not a fast operation, it's better if we don't have
    structure_guard_mutex locked during data coping.
    Thus we first allocate space and lock query, then unlock
    structure_guard_mutex and copy data.
  */

2184 2185
  my_bool success = allocate_data_chain(result_block, data_len, query_block,
					type == Query_cache_block::RES_BEG);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2186 2187
  if (success)
  {
2188
    // It is success (nobody can prevent us write data)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2189
    STRUCT_UNLOCK(&structure_guard_mutex);
2190 2191
    uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
			ALIGN_SIZE(sizeof(Query_cache_result)));
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2192
#ifndef EMBEDDED_LIBRARY
2193
    Query_cache_block *block= *result_block;
2194
    uchar *rest= data;
2195
    // Now fill list of blocks that created by allocate_data_chain
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2196 2197 2198 2199
    do
    {
      block->type = type;
      ulong length = block->used - headers_len;
2200
      DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length,
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2201
			    (ulong)block));
2202
      memcpy((uchar*) block+headers_len, rest, length);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2203 2204 2205
      rest += length;
      block = block->next;
      type = Query_cache_block::RES_CONT;
2206
    } while (block != *result_block);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2207
#else
2208 2209 2210 2211 2212
    /*
      Set type of first block, emb_store_querycache_result() will handle
      the others.
    */
    (*result_block)->type= type;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2213 2214 2215
    Querycache_stream qs(*result_block, headers_len);
    emb_store_querycache_result(&qs, (THD*)data);
#endif /*!EMBEDDED_LIBRARY*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2216 2217 2218
  }
  else
  {
2219
    if (*result_block != 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2220
    {
2221 2222
      // Destroy list of blocks that was created & locked by lock_result_data
      Query_cache_block *block = *result_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2223 2224
      do
      {
2225
	Query_cache_block *current = block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2226 2227
	block = block->next;
	free_memory_block(current);
2228 2229
      } while (block != *result_block);
      *result_block = 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2230
      /*
2231 2232
	It is not success => not unlock structure_guard_mutex (we need it to
	free query)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2233 2234 2235
      */
    }
  }
2236
  DBUG_PRINT("qcache", ("success %d", (int) success));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2237 2238 2239
  DBUG_RETURN(success);
}

2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253
inline ulong Query_cache::get_min_first_result_data_size()
{
  if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER)
    return min_result_data_size;
  ulong avg_result = (query_cache_size - free_memory) / queries_in_cache;
  avg_result = min(avg_result, query_cache_limit);
  return max(min_result_data_size, avg_result);
}

inline ulong Query_cache::get_min_append_result_data_size()
{
  return min_result_data_size;
}

2254 2255 2256 2257
/*
  Allocate one or more blocks to hold data
*/
my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2258
					 ulong data_len,
2259
					 Query_cache_block *query_block,
2260
					 my_bool first_block_arg)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2261
{
2262 2263
  ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
			   ALIGN_SIZE(sizeof(Query_cache_result)));
2264
  ulong min_size = (first_block_arg ?
2265 2266
		    get_min_first_result_data_size():
		    get_min_append_result_data_size());
2267 2268 2269 2270 2271 2272 2273
  Query_cache_block *prev_block= NULL;
  Query_cache_block *new_block;
  DBUG_ENTER("Query_cache::allocate_data_chain");
  DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu",
			data_len, all_headers_len));

  do
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2274
  {
2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286
    ulong len= data_len + all_headers_len;
    ulong align_len= ALIGN_SIZE(len);

    if (!(new_block= allocate_block(max(min_size, align_len),
				    min_result_data_size == 0,
				    all_headers_len + min_result_data_size,
				    1)))
    {
      DBUG_PRINT("warning", ("Can't allocate block for results"));
      DBUG_RETURN(FALSE);
    }

2287
    new_block->n_tables = 0;
2288
    new_block->used = min(len, new_block->length);
2289 2290 2291
    new_block->type = Query_cache_block::RES_INCOMPLETE;
    new_block->next = new_block->prev = new_block;
    Query_cache_result *header = new_block->result();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2292 2293
    header->parent(query_block);

2294
    DBUG_PRINT("qcache", ("Block len %lu used %lu",
2295
			  new_block->length, new_block->used));
2296 2297 2298

    if (prev_block)
      double_linked_list_join(prev_block, new_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2299
    else
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309
      *result_block= new_block;
    if (new_block->length >= len)
      break;

    /*
      We got less memory then we need (no big memory blocks) =>
      Continue to allocated more blocks until we got everything we need.
    */
    data_len= len - new_block->length;
    prev_block= new_block;
monty@mishka.local's avatar
monty@mishka.local committed
2310
  } while (1);
2311 2312

  DBUG_RETURN(TRUE);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2313 2314 2315
}

/*****************************************************************************
2316 2317 2318 2319 2320 2321
  Tables management
*****************************************************************************/

/*
  Invalidate the first table in the table_list
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2322 2323 2324 2325

void Query_cache::invalidate_table(TABLE_LIST *table_list)
{
  if (table_list->table != 0)
2326
    invalidate_table(table_list->table);	// Table is open
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2327 2328
  else
  {
2329
    char key[MAX_DBKEY_LENGTH];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2330 2331 2332
    uint key_length;
    Query_cache_block *table_block;
    key_length=(uint) (strmov(strmov(key,table_list->db)+1,
2333
			      table_list->table_name) -key)+ 1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2334

2335
    // We don't store temporary tables => no key_length+=4 ...
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2336
    if ((table_block = (Query_cache_block*)
2337
	 hash_search(&tables,(uchar*) key,key_length)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2338 2339 2340 2341 2342
      invalidate_table(table_block);
  }
}

void Query_cache::invalidate_table(TABLE *table)
2343
{
2344
  invalidate_table((uchar*) table->s->table_cache_key.str,
2345
                   table->s->table_cache_key.length);
2346 2347
}

2348
void Query_cache::invalidate_table(uchar * key, uint32  key_length)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2349 2350
{
  Query_cache_block *table_block;
2351
  if ((table_block = ((Query_cache_block*)
2352
		      hash_search(&tables, key, key_length))))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2353 2354 2355 2356 2357
    invalidate_table(table_block);
}

void Query_cache::invalidate_table(Query_cache_block *table_block)
{
2358 2359
  Query_cache_block_table *list_root =	table_block->table(0);
  while (list_root->next != list_root)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2360
  {
2361
    Query_cache_block *query_block = list_root->next->block();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2362 2363 2364 2365 2366
    BLOCK_LOCK_WR(query_block);
    free_query(query_block);
  }
}

2367

2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382
/*
  Register given table list begining with given position in tables table of
  block

  SYNOPSIS
    Query_cache::register_tables_from_list
    tables_used     given table list
    counter         number current position in table of tables of block
    block_table     pointer to current position in tables table of block

  RETURN
    0   error
    number of next position of table entry in table of tables of block
*/

2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393
TABLE_COUNTER_TYPE
Query_cache::register_tables_from_list(TABLE_LIST *tables_used,
                                       TABLE_COUNTER_TYPE counter,
                                       Query_cache_block_table *block_table)
{
  TABLE_COUNTER_TYPE n;
  DBUG_ENTER("Query_cache::register_tables_from_list");
  for (n= counter;
       tables_used;
       tables_used= tables_used->next_global, n++, block_table++)
  {
2394
    if (tables_used->derived && !tables_used->view)
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2395
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2396
      DBUG_PRINT("qcache", ("derived table skipped"));
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2397 2398 2399 2400
      n--;
      block_table--;
      continue;
    }
2401 2402 2403 2404 2405
    block_table->n= n;
    if (tables_used->view)
    {
      char key[MAX_DBKEY_LENGTH];
      uint key_length;
2406
      DBUG_PRINT("qcache", ("view: %s  db: %s",
2407 2408 2409 2410
                            tables_used->view_name.str,
                            tables_used->view_db.str));
      key_length= (uint) (strmov(strmov(key, tables_used->view_db.str) + 1,
                                 tables_used->view_name.str) - key) + 1;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2411 2412 2413 2414 2415 2416 2417
      /*
        There are not callback function for for VIEWs
      */
      if (!insert_table(key_length, key, block_table,
                        tables_used->view_db.length + 1,
                        HA_CACHE_TBL_NONTRANSACT, 0, 0))
        DBUG_RETURN(0);
2418 2419 2420 2421
      /*
        We do not need to register view tables here because they are already
        present in the global list.
      */
2422 2423 2424 2425
    }
    else
    {
      DBUG_PRINT("qcache",
2426
                 ("table: %s  db: %s  openinfo:  0x%lx  keylen: %lu  key: 0x%lx",
2427 2428
                  tables_used->table->s->table_name.str,
                  tables_used->table->s->table_cache_key.str,
2429
                  (ulong) tables_used->table,
2430
                  (ulong) tables_used->table->s->table_cache_key.length,
2431
                  (ulong) tables_used->table->s->table_cache_key.str));
2432

2433 2434 2435
      if (!insert_table(tables_used->table->s->table_cache_key.length,
                        tables_used->table->s->table_cache_key.str,
                        block_table,
2436
                        tables_used->db_length,
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2437 2438 2439
                        tables_used->table->file->table_cache_type(),
                        tables_used->callback_func,
                        tables_used->engine_data))
2440 2441
        DBUG_RETURN(0);

antony@ppcg5.local's avatar
antony@ppcg5.local committed
2442 2443 2444 2445 2446 2447
#ifdef WITH_MYISAMMRG_STORAGE_ENGINE      
      /*
        XXX FIXME: Some generic mechanism is required here instead of this
        MYISAMMRG-specific implementation.
      */
      if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459
      {
        ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file;
        MYRG_INFO *file = handler->myrg_info();
        for (MYRG_TABLE *table = file->open_tables;
             table != file->end_table ;
             table++)
        {
          char key[MAX_DBKEY_LENGTH];
          uint32 db_length;
          uint key_length= filename_2_table_key(key, table->table->filename,
                                                &db_length);
          (++block_table)->n= ++n;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2460 2461 2462
          /*
            There are not callback function for for MyISAM, and engine data
          */
2463 2464
          if (!insert_table(key_length, key, block_table,
                            db_length,
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
2465 2466
                            tables_used->table->file->table_cache_type(),
                            0, 0))
2467 2468 2469
            DBUG_RETURN(0);
        }
      }
antony@ppcg5.local's avatar
antony@ppcg5.local committed
2470
#endif
2471 2472 2473 2474 2475
    }
  }
  DBUG_RETURN(n - counter);
}

2476 2477 2478 2479 2480 2481 2482 2483 2484
/*
  Store all used tables

  SYNOPSIS
    register_all_tables()
    block		Store tables in this block
    tables_used		List if used tables
    tables_arg		Not used ?
*/
2485 2486 2487

my_bool Query_cache::register_all_tables(Query_cache_block *block,
					 TABLE_LIST *tables_used,
2488
					 TABLE_COUNTER_TYPE tables_arg)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2489
{
2490 2491
  TABLE_COUNTER_TYPE n;
  DBUG_PRINT("qcache", ("register tables block 0x%lx, n %d, header %x",
2492
		      (ulong) block, (int) tables_arg,
2493
		      (int) ALIGN_SIZE(sizeof(Query_cache_block))));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2494

2495 2496
  Query_cache_block_table *block_table = block->table(0);

2497
  n= register_tables_from_list(tables_used, 0, block_table);
2498

2499
  if (n==0)
2500 2501 2502 2503 2504 2505
  {
    /* Unlink the tables we allocated above */
    for (Query_cache_block_table *tmp = block->table(0) ;
	 tmp != block_table;
	 tmp++)
      unlink_table(tmp);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2506
  }
2507
  return (n);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2508 2509
}

2510 2511 2512 2513 2514 2515 2516 2517
/*
  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,
2518 2519 2520
			  uint32 db_length, uint8 cache_type,
                          qc_engine_callback callback,
                          ulonglong engine_data)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2521 2522
{
  DBUG_ENTER("Query_cache::insert_table");
2523
  DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2524
		      (ulong)node, key_len));
2525 2526

  Query_cache_block *table_block = ((Query_cache_block *)
2527
				    hash_search(&tables, (uchar*) key,
2528
						key_len));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2529

2530 2531 2532 2533
  if (table_block &&
      table_block->table()->engine_data() != engine_data)
  {
    DBUG_PRINT("qcache",
2534
               ("Handler require invalidation queries of %s.%s %lu-%lu",
2535 2536
                table_block->table()->db(),
                table_block->table()->table(),
2537 2538
                (ulong) engine_data,
                (ulong) table_block->table()->engine_data()));
2539 2540 2541 2542 2543 2544 2545 2546
    /*
      as far as we delete all queries with this table, table block will be
      deleted, too
    */
    invalidate_table(table_block);
    table_block= 0;
  }

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2547 2548
  if (table_block == 0)
  {
2549 2550
    DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
			(ulong) key, (int) key_len));
2551
    table_block = write_block_data(key_len, (uchar*) key,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2552 2553 2554 2555 2556
				   ALIGN_SIZE(sizeof(Query_cache_table)),
				   Query_cache_block::TABLE,
				   1, 1);
    if (table_block == 0)
    {
2557
      DBUG_PRINT("qcache", ("Can't write table name to cache"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2558 2559
      DBUG_RETURN(0);
    }
2560
    Query_cache_table *header = table_block->table();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2561
    double_linked_list_simple_include(table_block,
2562
				      &tables_blocks);
2563
    Query_cache_block_table *list_root = table_block->table(0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2564 2565
    list_root->n = 0;
    list_root->next = list_root->prev = list_root;
2566
    if (my_hash_insert(&tables, (const uchar *) table_block))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2567
    {
2568
      DBUG_PRINT("qcache", ("Can't insert table to hash"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2569 2570 2571 2572
      // write_block_data return locked block
      free_memory_block(table_block);
      DBUG_RETURN(0);
    }
2573
    char *db = header->db();
2574
    header->table(db + db_length + 1);
2575 2576
    header->key_length(key_len);
    header->type(cache_type);
2577 2578
    header->callback(callback);
    header->engine_data(engine_data);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2579 2580
  }

2581 2582 2583 2584 2585
  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;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2586 2587 2588 2589
  node->parent = table_block->table();
  DBUG_RETURN(1);
}

2590 2591

void Query_cache::unlink_table(Query_cache_block_table *node)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2592
{
2593
  DBUG_ENTER("Query_cache::unlink_table");
2594 2595 2596
  node->prev->next = node->next;
  node->next->prev = node->prev;
  Query_cache_block_table *neighbour = node->next;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2597 2598
  if (neighbour->next == neighbour)
  {
2599 2600
    // list is empty (neighbor is root of list)
    Query_cache_block *table_block = neighbour->block();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2601
    double_linked_list_exclude(table_block,
2602
			       &tables_blocks);
2603
    hash_delete(&tables,(uchar *) table_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2604 2605
    free_memory_block(table_block);
  }
2606
  DBUG_VOID_RETURN;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2607 2608 2609
}

/*****************************************************************************
2610 2611
  Free memory management
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2612

2613 2614 2615
Query_cache_block *
Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
			    my_bool under_guard)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2616
{
2617
  DBUG_ENTER("Query_cache::allocate_block");
2618
  DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2619 2620 2621 2622
		      len, not_less,min,under_guard));

  if (len >= min(query_cache_size, query_cache_limit))
  {
2623
    DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2624 2625 2626 2627
			query_cache_size, query_cache_limit));
    DBUG_RETURN(0); // in any case we don't have such piece of memory
  }

2628
  if (!under_guard)
2629
  {
2630
    STRUCT_LOCK(&structure_guard_mutex);
2631 2632

    if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
2633 2634 2635 2636 2637
    {
      STRUCT_UNLOCK(&structure_guard_mutex);
      DBUG_RETURN(0);
    }
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2638

2639 2640 2641
  /* Free old queries until we have enough memory to store this block */
  Query_cache_block *block;
  do
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2642
  {
2643
    block= get_free_block(len, not_less, min);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2644
  }
2645
  while (block == 0 && !free_old_query());
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2646

2647
  if (block != 0)				// If we found a suitable block
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2648
  {
2649
    if (block->length >= ALIGN_SIZE(len) + min_allocation_unit)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2650 2651 2652
      split_block(block,ALIGN_SIZE(len));
  }

2653 2654
  if (!under_guard)
    STRUCT_UNLOCK(&structure_guard_mutex);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2655 2656 2657
  DBUG_RETURN(block);
}

2658 2659 2660

Query_cache_block *
Query_cache::get_free_block(ulong len, my_bool not_less, ulong min)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2661 2662
{
  Query_cache_block *block = 0, *first = 0;
2663 2664
  DBUG_ENTER("Query_cache::get_free_block");
  DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2665 2666
		     (int)not_less, min));

2667
  /* Find block with minimal size > len  */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2668 2669 2670 2671
  uint start = find_bin(len);
  // try matching bin
  if (bins[start].number != 0)
  {
2672
    Query_cache_block *list = bins[start].free_blocks;
2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694
    if (list->prev->length >= len) // check block with max size 
    { 
      first = list;
      uint n = 0;
      while ( n < QUERY_CACHE_MEM_BIN_TRY &&
	      first->length < len) //we don't need irst->next != list
      {
	first=first->next;
	n++;
      }
      if (first->length >= len)
	block=first;
      else // we don't need if (first->next != list)
      {
	n = 0;
	block = list->prev;
	while (n < QUERY_CACHE_MEM_BIN_TRY &&
	       block->length > len)
	{
	  block=block->prev;
	  n++;
	}
2695
	if (block->length < len)
2696 2697 2698 2699 2700
	  block=block->next;
      }
    }
    else
      first = list->prev;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2701 2702 2703
  }
  if (block == 0 && start > 0)
  {
2704 2705
    DBUG_PRINT("qcache",("Try bins with bigger block size"));
    // Try more big bins
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2706
    int i = start - 1;
2707
    while (i > 0 && bins[i].number == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2708 2709 2710 2711
      i--;
    if (bins[i].number > 0)
      block = bins[i].free_blocks;
  }
2712 2713

  // If no big blocks => try less size (if it is possible)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2714 2715
  if (block == 0 && ! not_less)
  {
2716
    DBUG_PRINT("qcache",("Try to allocate a smaller block"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2717 2718 2719 2720 2721
    if (first != 0 && first->length > min)
      block = first;
    else
    {
      uint i = start + 1;
2722 2723 2724
      /* 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)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2725 2726 2727 2728 2729 2730
	block = bins[i].free_blocks->prev;
    }
  }
  if (block != 0)
    exclude_from_free_memory_list(block);

2731
  DBUG_PRINT("qcache",("getting block 0x%lx", (ulong) block));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2732 2733 2734
  DBUG_RETURN(block);
}

2735 2736

void Query_cache::free_memory_block(Query_cache_block *block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2737
{
2738
  DBUG_ENTER("Query_cache::free_memory_block");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2739
  block->used=0;
2740 2741 2742
  block->type= Query_cache_block::FREE; // mark block as free in any case
  DBUG_PRINT("qcache",
	     ("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx",
2743
	      (ulong) first_block, (ulong) block, (ulong) block->pnext,
2744
	      (ulong) block->pprev));
2745

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2746 2747 2748 2749 2750 2751 2752 2753
  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;
}

2754

2755
void Query_cache::split_block(Query_cache_block *block, ulong len)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2756 2757
{
  DBUG_ENTER("Query_cache::split_block");
2758
  Query_cache_block *new_block = (Query_cache_block*)(((uchar*) block)+len);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2759 2760

  new_block->init(block->length - len);
2761
  total_blocks++;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2762
  block->length=len;
2763 2764 2765 2766
  new_block->pnext = block->pnext;
  block->pnext = new_block;
  new_block->pprev = block;
  new_block->pnext->pprev = new_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2767

2768
  if (block->type == Query_cache_block::FREE)
2769
  {
2770 2771
    // if block was free then it already joined with all free neighbours
    insert_into_free_memory_list(new_block);
2772
  }
2773 2774
  else
    free_memory_block(new_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2775

2776
  DBUG_PRINT("qcache", ("split 0x%lx (%lu) new 0x%lx",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2777 2778 2779 2780
		      (ulong) block, len, (ulong) new_block));
  DBUG_VOID_RETURN;
}

2781

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2782
Query_cache_block *
2783
Query_cache::join_free_blocks(Query_cache_block *first_block_arg,
2784
			      Query_cache_block *block_in_list)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2785
{
2786
  Query_cache_block *second_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2787
  DBUG_ENTER("Query_cache::join_free_blocks");
2788
  DBUG_PRINT("qcache",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2789
	     ("join first 0x%lx, pnext 0x%lx, in list 0x%lx",
2790
	      (ulong) first_block_arg, (ulong) first_block_arg->pnext,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2791 2792
	      (ulong) block_in_list));

2793
  exclude_from_free_memory_list(block_in_list);
2794
  second_block = first_block_arg->pnext;
2795 2796
  // May be was not free block
  second_block->used=0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2797
  second_block->destroy();
2798
  total_blocks--;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2799

2800 2801 2802
  first_block_arg->length += second_block->length;
  first_block_arg->pnext = second_block->pnext;
  second_block->pnext->pprev = first_block_arg;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2803

2804
  DBUG_RETURN(first_block_arg);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2805 2806
}

2807 2808

my_bool Query_cache::append_next_free_block(Query_cache_block *block,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2809 2810
					    ulong add_size)
{
2811
  Query_cache_block *next_block = block->pnext;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2812 2813 2814
  DBUG_ENTER("Query_cache::append_next_free_block");
  DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block,
		       add_size));
2815

2816
  if (next_block != first_block && next_block->is_free())
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2817 2818
  {
    ulong old_len = block->length;
2819
    exclude_from_free_memory_list(next_block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2820
    next_block->destroy();
2821
    total_blocks--;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2822 2823 2824 2825 2826

    block->length += next_block->length;
    block->pnext = next_block->pnext;
    next_block->pnext->pprev = block;

2827
    if (block->length > ALIGN_SIZE(old_len + add_size) + min_allocation_unit)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2828 2829 2830 2831
      split_block(block,ALIGN_SIZE(old_len + add_size));
    DBUG_PRINT("exit", ("block was appended"));
    DBUG_RETURN(1);
  }
2832
  DBUG_RETURN(0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2833 2834
}

2835 2836

void Query_cache::exclude_from_free_memory_list(Query_cache_block *free_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2837 2838
{
  DBUG_ENTER("Query_cache::exclude_from_free_memory_list");
2839 2840 2841
  Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
				  free_block->data());
  double_linked_list_exclude(free_block, &bin->free_blocks);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2842 2843
  bin->number--;
  free_memory-=free_block->length;
2844
  free_memory_blocks--;
2845 2846
  DBUG_PRINT("qcache",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block,
		     (ulong) bin));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2847 2848 2849
  DBUG_VOID_RETURN;
}

2850
void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2851 2852 2853
{
  DBUG_ENTER("Query_cache::insert_into_free_memory_list");
  uint idx = find_bin(free_block->length);
2854
  insert_into_free_memory_sorted_list(free_block, &bins[idx].free_blocks);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2855
  /*
2856
    We have enough memory in block for storing bin reference due to
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2857 2858
    min_allocation_unit choice
  */
2859 2860 2861 2862 2863 2864
  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));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2865 2866 2867 2868 2869 2870
  DBUG_VOID_RETURN;
}

uint Query_cache::find_bin(ulong size)
{
  DBUG_ENTER("Query_cache::find_bin");
2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881
  // Binary search
  int left = 0, right = mem_bin_steps;
  do
  {
    int middle = (left + right) / 2;
    if (steps[middle].size > size)
      left = middle+1;
    else
      right = middle;
  } while (left < right);
  if (left == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2882 2883
  {
    // first bin not subordinate of common rules
2884
    DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2885 2886
    DBUG_RETURN(0);
  }
2887 2888
  uint bin =  steps[left].idx - 
    (uint)((size - steps[left].size)/steps[left].increment);
2889
#ifndef DBUG_OFF
2890
  bins_dump();
2891
#endif
2892 2893
  DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
			bin, left, size, steps[left].size));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2894 2895 2896
  DBUG_RETURN(bin);
}

2897

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2898
/*****************************************************************************
2899 2900
 Lists management
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2901

2902
void Query_cache::move_to_query_list_end(Query_cache_block *query_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2903 2904
{
  DBUG_ENTER("Query_cache::move_to_query_list_end");
2905 2906
  double_linked_list_exclude(query_block, &queries_blocks);
  double_linked_list_simple_include(query_block, &queries_blocks);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2907 2908 2909
  DBUG_VOID_RETURN;
}

2910

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2911 2912
void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block *
						      new_block,
2913 2914
						      Query_cache_block **
						      list)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2915 2916 2917 2918
{
  DBUG_ENTER("Query_cache::insert_into_free_memory_sorted_list");
  /*
     list sorted by size in ascendant order, because we need small blocks
2919
     more frequently than bigger ones
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2920 2921 2922 2923 2924 2925
  */

  new_block->used = 0;
  new_block->n_tables = 0;
  new_block->type = Query_cache_block::FREE;

2926
  if (*list == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2927
  {
2928 2929
    *list = new_block->next=new_block->prev=new_block;
    DBUG_PRINT("qcache", ("inserted into empty list"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2930 2931 2932
  }
  else
  {
2933
    Query_cache_block *point = *list;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2934 2935 2936
    if (point->length >= new_block->length)
    {
      point = point->prev;
2937
      *list = new_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2938 2939 2940
    }
    else
    {
2941 2942 2943
      /* Find right position in sorted list to put block */
      while (point->next != *list &&
	     point->next->length < new_block->length)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2944 2945
	point=point->next;
    }
2946 2947 2948 2949
    new_block->prev = point;
    new_block->next = point->next;
    new_block->next->prev = new_block;
    point->next = new_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2950 2951
  }
  free_memory+=new_block->length;
2952
  free_memory_blocks++;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2953 2954 2955 2956 2957
  DBUG_VOID_RETURN;
}


void
2958 2959 2960
Query_cache::double_linked_list_simple_include(Query_cache_block *point,
						Query_cache_block **
						list_pointer)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2961 2962
{
  DBUG_ENTER("Query_cache::double_linked_list_simple_include");
2963 2964 2965
  DBUG_PRINT("qcache", ("including block 0x%lx", (ulong) point));
  if (*list_pointer == 0)
    *list_pointer=point->next=point->prev=point;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2966 2967
  else
  {
2968
    // insert to the end of list
2969 2970
    point->next = (*list_pointer);
    point->prev = (*list_pointer)->prev;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2971
    point->prev->next = point;
2972
    (*list_pointer)->prev = point;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2973 2974 2975 2976 2977 2978
  }
  DBUG_VOID_RETURN;
}

void
Query_cache::double_linked_list_exclude(Query_cache_block *point,
2979
					Query_cache_block **list_pointer)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2980 2981
{
  DBUG_ENTER("Query_cache::double_linked_list_exclude");
2982
  DBUG_PRINT("qcache", ("excluding block 0x%lx, list 0x%lx",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2983 2984
		      (ulong) point, (ulong) list_pointer));
  if (point->next == point)
2985
    *list_pointer = 0;				// empty list
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2986 2987 2988 2989
  else
  {
    point->next->prev = point->prev;
    point->prev->next = point->next;
2990 2991 2992
    /*
       If the root is removed; select a new root
    */
2993
    if (point == *list_pointer)
2994
      *list_pointer= point->next;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2995 2996 2997 2998
  }
  DBUG_VOID_RETURN;
}

2999

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3000 3001 3002 3003
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,
3004
		    *tail_tail	= tail_head->prev;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3005 3006 3007 3008 3009 3010 3011
  head_head->prev = tail_tail;
  head_tail->next = tail_head;
  tail_head->prev = head_tail;
  tail_tail->next = head_head;
}

/*****************************************************************************
3012 3013
 Query
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3014

3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035
/*
  Collect information about table types, check that tables are cachable and
  count them

  SYNOPSIS
    process_and_count_tables()
    tables_used     table list for processing
    tables_type     pointer to variable for table types collection

  RETURN
    0   error
    >0  number of tables
*/

static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used,
                                                   uint8 *tables_type)
{
  DBUG_ENTER("process_and_count_tables");
  TABLE_COUNTER_TYPE table_count = 0;
  for (; tables_used; tables_used= tables_used->next_global)
  {
3036
    table_count++;
3037 3038
    if (tables_used->view)
    {
3039
      DBUG_PRINT("qcache", ("view: %s  db: %s",
3040 3041 3042 3043 3044 3045
                            tables_used->view_name.str,
                            tables_used->view_db.str));
      *tables_type|= HA_CACHE_TBL_NONTRANSACT;
    }
    else
    {
3046 3047 3048
      DBUG_PRINT("qcache", ("table: %s  db:  %s  type: %u",
                            tables_used->table->s->table_name.str,
                            tables_used->table->s->db.str,
antony@ppcg5.local's avatar
antony@ppcg5.local committed
3049
                            tables_used->table->s->db_type()->db_type));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3050
      if (tables_used->derived)
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
3051 3052 3053 3054 3055
      {
        table_count--;
        DBUG_PRINT("qcache", ("derived table skipped"));
        continue;
      }
3056 3057 3058 3059 3060 3061 3062 3063 3064 3065
      *tables_type|= tables_used->table->file->table_cache_type();

      /*
        table_alias_charset used here because it depends of
        lower_case_table_names variable
      */
      if (tables_used->table->s->tmp_table != NO_TMP_TABLE ||
          (*tables_type & HA_CACHE_TBL_NOCACHE) ||
          (tables_used->db_length == 5 &&
           my_strnncoll(table_alias_charset,
3066
                        (uchar*)tables_used->table->s->table_cache_key.str, 6,
3067 3068 3069
                        (uchar*)"mysql",6) == 0))
      {
        DBUG_PRINT("qcache",
3070 3071
                   ("select not cacheable: temporary, system or "
                    "other non-cacheable table(s)"));
3072 3073
        DBUG_RETURN(0);
      }
antony@ppcg5.local's avatar
antony@ppcg5.local committed
3074 3075 3076 3077 3078 3079
#ifdef WITH_MYISAMMRG_STORAGE_ENGINE      
      /*
        XXX FIXME: Some generic mechanism is required here instead of this
        MYISAMMRG-specific implementation.
      */
      if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM)
3080 3081 3082 3083 3084
      {
        ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file;
        MYRG_INFO *file = handler->myrg_info();
        table_count+= (file->end_table - file->open_tables);
      }
antony@ppcg5.local's avatar
antony@ppcg5.local committed
3085
#endif
3086 3087 3088 3089 3090 3091
    }
  }
  DBUG_RETURN(table_count);
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3092
/*
3093
  If query is cacheable return number tables in query
3094
  (query without tables are not cached)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3095
*/
3096

3097 3098 3099
TABLE_COUNTER_TYPE
Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
                          TABLE_LIST *tables_used, uint8 *tables_type)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3100
{
3101
  TABLE_COUNTER_TYPE table_count;
3102 3103
  DBUG_ENTER("Query_cache::is_cacheable");

3104
  if (query_cache_is_cacheable_query(lex) &&
3105
      (thd->variables.query_cache_type == 1 ||
3106
       (thd->variables.query_cache_type == 2 && (lex->select_lex.options &
3107
						 OPTION_TO_QUERY_CACHE))))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3108
  {
3109
    DBUG_PRINT("qcache", ("options: %lx  %lx  type: %u",
3110
                          (long) OPTION_TO_QUERY_CACHE,
3111 3112
                          (long) lex->select_lex.options,
                          (int) thd->variables.query_cache_type));
3113

3114 3115
    if (!(table_count= process_and_count_tables(tables_used, tables_type)))
      DBUG_RETURN(0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3116

3117
    if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
3118
	((*tables_type)&HA_CACHE_TBL_TRANSACT))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3119
    {
3120
      DBUG_PRINT("qcache", ("not in autocommin mode"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3121 3122
      DBUG_RETURN(0);
    }
3123 3124
    DBUG_PRINT("qcache", ("select is using %d tables", table_count));
    DBUG_RETURN(table_count);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3125
  }
3126 3127

  DBUG_PRINT("qcache",
3128
	     ("not interesting query: %d or not cacheable, options %lx %lx  type: %u",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3129
	      (int) lex->sql_command,
3130
	      (long) OPTION_TO_QUERY_CACHE,
3131
	      (long) lex->select_lex.options,
3132
	      (int) thd->variables.query_cache_type));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3133 3134 3135
  DBUG_RETURN(0);
}

3136
/*
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3137
  Check handler allowance to cache query with these tables
3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150

  SYNOPSYS
    Query_cache::ask_handler_allowance()
    thd - thread handlers
    tables_used - tables list used in query

  RETURN
    0 - caching allowed
    1 - caching disallowed
*/
my_bool Query_cache::ask_handler_allowance(THD *thd,
					   TABLE_LIST *tables_used)
{
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3151
  DBUG_ENTER("Query_cache::ask_handler_allowance");
3152

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3153
  for (; tables_used; tables_used= tables_used->next_global)
3154
  {
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
3155
    TABLE *table;
3156
    handler *handler;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
3157 3158
    if (!(table= tables_used->table))
      continue;
3159 3160 3161 3162
    handler= table->file;
    if (!handler->register_query_cache_table(thd,
                                             table->s->table_cache_key.str,
					     table->s->table_cache_key.length,
3163 3164
					     &tables_used->callback_func,
					     &tables_used->engine_data))
3165 3166 3167
    {
      DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
			    tables_used->db, tables_used->alias));
pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
3168
      thd->lex->safe_to_cache_query= 0;          // Don't try to cache this
3169 3170 3171 3172 3173 3174
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN(0);
}

3175

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3176
/*****************************************************************************
3177 3178
  Packing
*****************************************************************************/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3179 3180 3181

void Query_cache::pack_cache()
{
3182
  DBUG_ENTER("Query_cache::pack_cache");
3183

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3184
  STRUCT_LOCK(&structure_guard_mutex);
3185 3186

  if (unlikely(query_cache_size == 0 || flush_in_progress))
3187 3188 3189 3190 3191
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    DBUG_VOID_RETURN;
  }

3192 3193
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););

3194
  uchar *border = 0;
3195
  Query_cache_block *before = 0;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3196 3197
  ulong gap = 0;
  my_bool ok = 1;
3198
  Query_cache_block *block = first_block;
3199 3200
  DUMP(this);

3201
  if (first_block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3202
  {
3203 3204
    do
    {
3205
      Query_cache_block *next=block->pnext;
3206
      ok = move_by_type(&border, &before, &gap, block);
3207
      block = next;
3208
    } while (ok && block != first_block);
3209

3210 3211 3212 3213
    if (border != 0)
    {
      Query_cache_block *new_block = (Query_cache_block *) border;
      new_block->init(gap);
3214
      total_blocks++;
3215 3216 3217 3218 3219 3220 3221
      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);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3222
  }
3223 3224

  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3225 3226 3227 3228
  STRUCT_UNLOCK(&structure_guard_mutex);
  DBUG_VOID_RETURN;
}

3229

3230
my_bool Query_cache::move_by_type(uchar **border,
3231 3232
				  Query_cache_block **before, ulong *gap,
				  Query_cache_block *block)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3233 3234
{
  DBUG_ENTER("Query_cache::move_by_type");
3235

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3236
  my_bool ok = 1;
3237
  switch (block->type) {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3238
  case Query_cache_block::FREE:
3239 3240 3241
  {
    DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block));
    if (*border == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3242
    {
3243
      *border = (uchar *) block;
3244 3245
      *before = block->pprev;
      DBUG_PRINT("qcache", ("gap beginning here"));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3246
    }
3247 3248 3249 3250 3251
    exclude_from_free_memory_list(block);
    *gap +=block->length;
    block->pprev->pnext=block->pnext;
    block->pnext->pprev=block->pprev;
    block->destroy();
3252
    total_blocks--;
3253 3254 3255
    DBUG_PRINT("qcache", ("added to gap (%lu)", *gap));
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3256
  case Query_cache_block::TABLE:
3257
  {
3258
    HASH_SEARCH_STATE record_idx;
3259 3260
    DBUG_PRINT("qcache", ("block 0x%lx TABLE", (ulong) block));
    if (*border == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3261
      break;
3262 3263 3264 3265 3266 3267 3268 3269 3270
    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;
3271
    uint tablename_offset = block->table()->table() - block->table()->db();
3272
    char *data = (char*) block->data();
3273 3274 3275 3276
    uchar *key;
    size_t key_length;
    key=query_cache_table_get_key((uchar*) block, &key_length, 0);
    hash_first(&tables, (uchar*) key, key_length, &record_idx);
3277 3278 3279 3280 3281 3282

    block->destroy();
    new_block->init(len);
    new_block->type=Query_cache_block::TABLE;
    new_block->used=used;
    new_block->n_tables=1;
3283
    memmove((char*) new_block->data(), data, len-new_block->headers_len());
3284
    relink(block, new_block, next, prev, pnext, pprev);
3285 3286
    if (tables_blocks == block)
      tables_blocks = new_block;
3287 3288 3289

    Query_cache_block_table *nlist_root = new_block->table(0);
    nlist_root->n = 0;
3290 3291 3292 3293 3294 3295 3296 3297
    nlist_root->next = tnext;
    tnext->prev = nlist_root;
    nlist_root->prev = tprev;
    tprev->next = nlist_root;
    DBUG_PRINT("qcache",
	       ("list_root: 0x%lx tnext 0x%lx tprev 0x%lx tprev->next 0x%lx tnext->prev 0x%lx",
		(ulong) list_root, (ulong) tnext, (ulong) tprev,
		(ulong)tprev->next, (ulong)tnext->prev));
3298 3299 3300 3301 3302
    /*
      Go through all queries that uses this table and change them to
      point to the new table object
    */
    Query_cache_table *new_block_table=new_block->table();
3303
    for (;tnext != nlist_root; tnext=tnext->next)
3304
      tnext->parent= new_block_table;
3305 3306
    *border += len;
    *before = new_block;
3307 3308
    /* Fix pointer to table name */
    new_block->table()->table(new_block->table()->db() + tablename_offset);
3309
    /* Fix hash to point at moved block */
3310
    hash_replace(&tables, &record_idx, (uchar*) new_block);
3311 3312 3313 3314 3315

    DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
			len, (ulong) new_block, (ulong) *border));
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3316
  case Query_cache_block::QUERY:
3317
  {
3318
    HASH_SEARCH_STATE record_idx;
3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332
    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();
3333 3334 3335 3336
    uchar *key;
    size_t key_length;
    key=query_cache_query_get_key((uchar*) block, &key_length, 0);
    hash_first(&queries, (uchar*) key, key_length, &record_idx);
3337 3338
    // Move table of used tables 
    memmove((char*) new_block->table(0), (char*) block->table(0),
3339 3340 3341 3342 3343 3344 3345
	   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;
3346
    memmove((char*) new_block->data(), data, len - new_block->headers_len());
3347 3348 3349
    relink(block, new_block, next, prev, pnext, pprev);
    if (queries_blocks == block)
      queries_blocks = new_block;
3350 3351
    Query_cache_block_table *beg_of_table_table= block->table(0),
      *end_of_table_table= block->table(n_tables);
3352
    uchar *beg_of_new_table_table= (uchar*) new_block->table(0);
3353
      
3354
    for (TABLE_COUNTER_TYPE j=0; j < n_tables; j++)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3355
    {
3356
      Query_cache_block_table *block_table = new_block->table(j);
3357 3358 3359 3360 3361

      // use aligment from begining of table if 'next' is in same block
      if ((beg_of_table_table <= block_table->next) &&
	  (block_table->next < end_of_table_table))
	((Query_cache_block_table *)(beg_of_new_table_table + 
3362 3363
				     (((uchar*)block_table->next) -
				      ((uchar*)beg_of_table_table))))->prev=
3364 3365 3366 3367 3368 3369 3370 3371
	 block_table;
      else
	block_table->next->prev= block_table;

      // use aligment from begining of table if 'prev' is in same block
      if ((beg_of_table_table <= block_table->prev) &&
	  (block_table->prev < end_of_table_table))
	((Query_cache_block_table *)(beg_of_new_table_table + 
3372 3373
				     (((uchar*)block_table->prev) -
				      ((uchar*)beg_of_table_table))))->next=
3374 3375 3376
	  block_table;
      else
	block_table->prev->next = block_table;
3377 3378 3379 3380 3381 3382 3383 3384 3385
    }
    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
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3386
      {
3387 3388 3389 3390
	result_block->result()->parent(new_block);
	result_block = result_block->next;
      } while ( result_block != first_result_block );
    }
3391
    Query_cache_query *new_query= ((Query_cache_query *) new_block->data());
3392
    my_rwlock_init(&new_query->lock, NULL);
3393

3394 3395 3396 3397
    /* 
      If someone is writing to this block, inform the writer that the block
      has been moved.
    */
3398 3399 3400
    NET *net = new_block->query()->writer();
    if (net != 0)
    {
3401
      net->query_cache_query= (uchar*) new_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3402
    }
3403
    /* Fix hash to point at moved block */
3404
    hash_replace(&queries, &record_idx, (uchar*) new_block);
3405 3406 3407 3408
    DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
			len, (ulong) new_block, (ulong) *border));
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3409 3410 3411 3412
  case Query_cache_block::RES_INCOMPLETE:
  case Query_cache_block::RES_BEG:
  case Query_cache_block::RES_CONT:
  case Query_cache_block::RESULT:
3413 3414 3415 3416
  {
    DBUG_PRINT("qcache", ("block 0x%lx RES* (%d)", (ulong) block,
			(int) block->type));
    if (*border == 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3417
      break;
3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431
    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;
3432
    memmove((char*) new_block->data(), data, len - new_block->headers_len());
3433 3434 3435 3436 3437 3438 3439 3440
    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 */
3441 3442
    ulong free_space= new_block->length - new_block->used;
    free_space-= free_space % ALIGN_SIZE(1);
3443 3444 3445 3446 3447
    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)
    {
3448 3449 3450 3451
      *border-= free_space;
      *gap+= free_space;
      DBUG_PRINT("qcache",
		 ("rest of result free space added to gap (%lu)", *gap));
3452
      new_block->length -= free_space;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3453
    }
3454 3455 3456 3457 3458
    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;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3459
  default:
3460 3461
    DBUG_PRINT("error", ("unexpected block type %d, block 0x%lx",
			 (int)block->type, (ulong) block));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3462 3463 3464 3465 3466
    ok = 0;
  }
  DBUG_RETURN(ok);
}

3467 3468 3469 3470 3471

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)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3472
{
3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487
  if (prev == oblock) //check pointer to himself
  {
    nblock->prev = nblock;
    nblock->next = nblock;
  }
  else
  {
    nblock->prev = prev;
    prev->next=nblock;
  }
  if (next != oblock)
  {
    nblock->next = next;
    next->prev=nblock;
  }
3488
  nblock->pprev = pprev; // Physical pointer to himself have only 1 free block
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3489 3490 3491 3492 3493
  nblock->pnext = pnext;
  pprev->pnext=nblock;
  pnext->pprev=nblock;
}

3494

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3495 3496 3497
my_bool Query_cache::join_results(ulong join_limit)
{
  my_bool has_moving = 0;
3498 3499
  DBUG_ENTER("Query_cache::join_results");

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3500
  STRUCT_LOCK(&structure_guard_mutex);
3501
  if (queries_blocks != 0 && !flush_in_progress)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3502
  {
3503
    DBUG_ASSERT(query_cache_size > 0);
3504
    Query_cache_block *block = queries_blocks;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3505 3506
    do
    {
3507
      Query_cache_query *header = block->query();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3508 3509 3510 3511 3512
      if (header->result() != 0 &&
	  header->result()->type == Query_cache_block::RESULT &&
	  header->length() > join_limit)
      {
	Query_cache_block *new_result_block =
3513
	  get_free_block(ALIGN_SIZE(header->length()) +
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3514 3515 3516 3517 3518 3519
			 ALIGN_SIZE(sizeof(Query_cache_block)) +
			 ALIGN_SIZE(sizeof(Query_cache_result)), 1, 0);
	if (new_result_block != 0)
	{
	  has_moving = 1;
	  Query_cache_block *first_result = header->result();
3520 3521 3522
	  ulong new_len = (header->length() +
			   ALIGN_SIZE(sizeof(Query_cache_block)) +
			   ALIGN_SIZE(sizeof(Query_cache_result)));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3523 3524
	  if (new_result_block->length >
	      ALIGN_SIZE(new_len) + min_allocation_unit)
3525 3526
	    split_block(new_result_block, ALIGN_SIZE(new_len));
	  BLOCK_LOCK_WR(block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3527 3528 3529 3530 3531 3532
	  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;
3533
	  DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)",
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3534 3535 3536 3537 3538
			      new_result_block->length,
			      new_result_block->used,
			      header->length()));

	  Query_cache_result *new_result = new_result_block->result();
3539
	  new_result->parent(block);
3540
	  uchar *write_to = (uchar*) new_result->data();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3541 3542 3543
	  Query_cache_block *result_block = first_result;
	  do
	  {
3544 3545
	    ulong len = (result_block->used - result_block->headers_len() -
			 ALIGN_SIZE(sizeof(Query_cache_result)));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3546 3547 3548 3549 3550 3551 3552 3553
	    DBUG_PRINT("loop", ("add block %lu/%lu (%lu)",
				result_block->length,
				result_block->used,
				len));
	    memcpy((char *) write_to,
		   (char*) result_block->result()->data(),
		   len);
	    write_to += len;
3554
	    Query_cache_block *old_result_block = result_block;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3555 3556 3557
	    result_block = result_block->next;
	    free_memory_block(old_result_block);
	  } while (result_block != first_result);
3558
	  BLOCK_UNLOCK_WR(block);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3559 3560
	}
      }
3561 3562
      block = block->next;
    } while ( block != queries_blocks );
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3563 3564 3565 3566 3567
  }
  STRUCT_UNLOCK(&structure_guard_mutex);
  DBUG_RETURN(has_moving);
}

3568

3569 3570
uint Query_cache::filename_2_table_key (char *key, const char *path,
					uint32 *db_length)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3571
{
3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582
  char tablename[FN_REFLEN+2], *filename, *dbname;
  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--) ;
3583 3584
  *db_length= (filename - dbname) - 1;
  DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename));
3585

3586
  DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1,
3587 3588 3589 3590 3591 3592 3593
			     filename) -key) + 1);
}

/****************************************************************************
  Functions to be used when debugging
****************************************************************************/

3594 3595
#if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK)

3596
void wreck(uint line, const char *message) { query_cache_size = 0; }
3597 3598 3599 3600
void bins_dump() {}
void cache_dump() {}
void queries_dump() {}
void tables_dump() {}
3601
my_bool check_integrity(bool not_locked) { return 0; }
3602 3603 3604 3605 3606
my_bool in_list(Query_cache_block * root, Query_cache_block * point,
		const char *name) { return 0;}
my_bool in_blocks(Query_cache_block * point) { return 0; }

#else
3607

3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618

/*
  Debug method which switch query cache off but left content for
  investigation.

  SYNOPSIS
    Query_cache::wreck()
    line                 line of the wreck() call
    message              message for logging
*/

3619 3620
void Query_cache::wreck(uint line, const char *message)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3621
  THD *thd=current_thd;
3622 3623 3624 3625 3626 3627 3628
  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", ("=================================="));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3629
  if (thd)
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
3630
    thd->killed= THD::KILL_CONNECTION;
3631
  cache_dump();
3632
  /* check_integrity(0); */ /* Can't call it here because of locks */
3633
  bins_dump();
3634 3635 3636 3637 3638 3639 3640
  DBUG_VOID_RETURN;
}


void Query_cache::bins_dump()
{
  uint i;
3641
  
3642
  if (!initialized || query_cache_size == 0)
3643 3644 3645 3646 3647
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682
  DBUG_PRINT("qcache", ("mem_bin_num=%u, mem_bin_steps=%u",
		      mem_bin_num, mem_bin_steps));
  DBUG_PRINT("qcache", ("-------------------------"));
  DBUG_PRINT("qcache", ("      size idx       step"));
  DBUG_PRINT("qcache", ("-------------------------"));
  for (i=0; i < mem_bin_steps; i++)
  {
    DBUG_PRINT("qcache", ("%10lu %3d %10lu", steps[i].size, steps[i].idx,
			steps[i].increment));
  }
  DBUG_PRINT("qcache", ("-------------------------"));
  DBUG_PRINT("qcache", ("      size num"));
  DBUG_PRINT("qcache", ("-------------------------"));
  for (i=0; i < mem_bin_num; i++)
  {
    DBUG_PRINT("qcache", ("%10lu %3d 0x%lx", bins[i].size, bins[i].number,
			(ulong)&(bins[i])));
    if (bins[i].free_blocks)
    {
      Query_cache_block *block = bins[i].free_blocks;
      do{
	DBUG_PRINT("qcache", ("\\-- %lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx",
			    block->length, (ulong)block,
			    (ulong)block->next, (ulong)block->prev,
			    (ulong)block->pnext, (ulong)block->pprev));
	block = block->next;
      } while ( block != bins[i].free_blocks );
    }
  }
  DBUG_PRINT("qcache", ("-------------------------"));
}


void Query_cache::cache_dump()
{
3683
  if (!initialized || query_cache_size == 0)
3684 3685 3686 3687 3688
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705
  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", ("-------------------------------------"));
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3706

3707 3708
void Query_cache::queries_dump()
{
3709

3710
  if (!initialized)
3711 3712 3713 3714 3715
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

3716 3717 3718 3719 3720 3721 3722 3723
  DBUG_PRINT("qcache", ("------------------"));
  DBUG_PRINT("qcache", (" QUERIES"));
  DBUG_PRINT("qcache", ("------------------"));
  if (queries_blocks != 0)
  {
    Query_cache_block *block = queries_blocks;
    do
    {
3724 3725
      size_t len;
      char *str = (char*) query_cache_query_get_key((uchar*) block, &len, 0);
3726 3727 3728 3729
      len-= QUERY_CACHE_FLAGS_SIZE;		  // Point at flags
      Query_cache_query_flags flags;
      memcpy(&flags, str+len, QUERY_CACHE_FLAGS_SIZE);
      str[len]= 0; // make zero ending DB name
3730
      DBUG_PRINT("qcache", ("F: %u  C: %u L: %lu  T: '%s' (%lu)  '%s'  '%s'",
3731
			    flags.client_long_flag,
3732
			    flags.character_set_client_num, 
3733 3734
                            (ulong)flags.limit,
                            flags.time_zone->get_name()->ptr(),
3735
			    (ulong) len, str, strend(str)+1));
3736 3737 3738
      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));
3739 3740
      memcpy(str + len, &flags, QUERY_CACHE_FLAGS_SIZE); // restore flags
      for (TABLE_COUNTER_TYPE t= 0; t < block->n_tables; t++)
3741
      {
3742
	Query_cache_table *table= block->table(t)->parent;
3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770
	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", ("------------------"));
}
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3771 3772


3773 3774
void Query_cache::tables_dump()
{
3775
  if (!initialized || query_cache_size == 0)
3776 3777 3778 3779 3780
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

3781 3782 3783
  DBUG_PRINT("qcache", ("--------------------"));
  DBUG_PRINT("qcache", ("TABLES"));
  DBUG_PRINT("qcache", ("--------------------"));
3784
  if (tables_blocks != 0)
3785
  {
3786 3787
    Query_cache_block *table_block = tables_blocks;
    do
3788
    {
3789 3790 3791
      Query_cache_table *table = table_block->table();
      DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table()));
      table_block = table_block->next;
3792
    } while (table_block != tables_blocks);
3793
  }
3794 3795
  else
    DBUG_PRINT("qcache", ("no tables in list"));
3796
  DBUG_PRINT("qcache", ("--------------------"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3797
}
3798 3799


3800
my_bool Query_cache::check_integrity(bool locked)
3801 3802 3803
{
  my_bool result = 0;
  uint i;
3804
  DBUG_ENTER("check_integrity");
3805

3806 3807 3808 3809
  if (!locked)
    STRUCT_LOCK(&structure_guard_mutex);

  if (unlikely(query_cache_size == 0 || flush_in_progress))
3810
  {
3811 3812 3813
    if (!locked)
      STRUCT_UNLOCK(&query_cache.structure_guard_mutex);

3814
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
3815
    DBUG_RETURN(0);
3816 3817
  }

3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836
  if (hash_check(&queries))
  {
    DBUG_PRINT("error", ("queries hash is damaged"));
    result = 1;
  }

  if (hash_check(&tables))
  {
    DBUG_PRINT("error", ("tables hash is damaged"));
    result = 1;
  }

  DBUG_PRINT("qcache", ("physical address check ..."));
  ulong free=0, used=0;
  Query_cache_block * block = first_block;
  do
  {
    DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			  (ulong) block, (uint) block->type));  
3837
    // Check allignment
3838 3839
    if ((((long)block) % (long) ALIGN_SIZE(1)) !=
	(((long)first_block) % (long)ALIGN_SIZE(1)))
3840 3841 3842
    {
      DBUG_PRINT("error",
		 ("block 0x%lx do not aligned by %d", (ulong) block,
3843
		  (int) ALIGN_SIZE(1)));
3844 3845
      result = 1;
    }
3846 3847 3848
    // Check memory allocation
    if (block->pnext == first_block) // Is it last block?
    {
3849 3850
      if (((uchar*)block) + block->length != 
	  ((uchar*)first_block) + query_cache_size)
3851 3852 3853 3854
      {
	DBUG_PRINT("error", 
		   ("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx",
		    (ulong) block, (uint) block->type, 
3855 3856
		    (ulong) (((uchar*)block) + block->length),
		    (ulong) (((uchar*)first_block) + query_cache_size)));
3857 3858 3859 3860
	result = 1;
      }
    }
    else
3861
      if (((uchar*)block) + block->length != ((uchar*)block->pnext))
3862 3863 3864 3865
      {
	DBUG_PRINT("error", 
		   ("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx",
		    (ulong) block, (uint) block->type, 
3866 3867
		    (ulong) (((uchar*)block) + block->length),
		    (ulong) ((uchar*)block->pnext)));
3868 3869
      }
    if (block->type == Query_cache_block::FREE)
3870
      free+= block->length;
3871
    else
3872
      used+= block->length;
3873 3874 3875 3876 3877 3878
    switch(block->type) {
    case Query_cache_block::FREE:
    {
      Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
				      block->data());
      //is it correct pointer?
3879 3880
      if (((uchar*)bin) < ((uchar*)bins) ||
	  ((uchar*)bin) >= ((uchar*)first_block))
3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891
      {
	DBUG_PRINT("error", 
		   ("free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx]",
		    (ulong) block, 
		    (ulong) bin,
		    (ulong) bins,
		    (ulong) first_block));
	result = 1;
      }
      else
      {
3892
	int idx = (((uchar*)bin) - ((uchar*)bins)) /
3893 3894 3895 3896 3897 3898 3899
	  sizeof(Query_cache_memory_bin);
	if (in_list(bins[idx].free_blocks, block, "free memory"))
	  result = 1;
      }
      break;
    }
    case Query_cache_block::TABLE:
3900
      if (in_list(tables_blocks, block, "tables"))
3901
	result = 1;
3902 3903
      if (in_table_list(block->table(0),  block->table(0), "table list root"))
	result = 1;
3904 3905
      break;
    case Query_cache_block::QUERY:
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3906
    {
3907 3908
      if (in_list(queries_blocks, block, "query"))
	result = 1;
3909 3910 3911 3912 3913
      for (TABLE_COUNTER_TYPE j=0; j < block->n_tables; j++)
      {
	Query_cache_block_table *block_table = block->table(j);
	Query_cache_block_table *block_table_root = 
	  (Query_cache_block_table *) 
3914
	  (((uchar*)block_table->parent) -
3915 3916 3917 3918 3919
	   ALIGN_SIZE(sizeof(Query_cache_block_table)));
	
    	if (in_table_list(block_table, block_table_root, "table list"))
    	  result = 1;
      }
3920
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3921
    }
3922
    case Query_cache_block::RES_INCOMPLETE:
3923 3924
      // This type of block can be not lincked yet (in multithread environment)
      break;
3925 3926 3927 3928 3929
    case Query_cache_block::RES_BEG:
    case Query_cache_block::RES_CONT:
    case Query_cache_block::RESULT:
    {
      Query_cache_block * query_block = block->result()->parent();
3930 3931
      if (((uchar*)query_block) < ((uchar*)first_block) ||
	  ((uchar*)query_block) >= (((uchar*)first_block) + query_cache_size))
3932 3933 3934 3935 3936 3937
      {
	DBUG_PRINT("error", 
		   ("result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx]",
		    (ulong) block,
		    (ulong) query_block,
		    (ulong) first_block,
3938
		    (ulong) (((uchar*)first_block) + query_cache_size)));
3939 3940 3941 3942
	result = 1;
      }
      else
      {
3943
	BLOCK_LOCK_RD(query_block);
3944 3945 3946 3947 3948
	if (in_list(queries_blocks, query_block, "query from results"))
	  result = 1;
	if (in_list(query_block->query()->result(), block,
		    "results"))
	  result = 1;
3949
	BLOCK_UNLOCK_RD(query_block);
3950 3951 3952 3953
      }
      break;
    }
    default:
3954 3955
      DBUG_PRINT("error", ("block 0x%lx have incorrect type %u",
                           (long) block, block->type));
3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984
      result = 1;
    }
    
    block = block->pnext;
  } while (block != first_block);
  
  if (used + free != query_cache_size)
  {
    DBUG_PRINT("error",
	       ("used memory (%lu) + free memory (%lu) !=  query_cache_size (%lu)",
		used, free, query_cache_size));
    result = 1;
  }
  
  if (free != free_memory)
  {
    DBUG_PRINT("error",
	       ("free memory (%lu) != free_memory (%lu)",
		free, free_memory));
    result = 1;
  }

  DBUG_PRINT("qcache", ("check queries ..."));
  if ((block = queries_blocks))
  {
    do
    {
      DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			    (ulong) block, (uint) block->type));
3985 3986 3987 3988
      size_t length;
      uchar *key = query_cache_query_get_key((uchar*) block, &length, 0);
      uchar* val = hash_search(&queries, key, length);
      if (((uchar*)block) != val)
3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013
      {
	DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx",
			     (ulong) block, (ulong) val));
      }
      if (in_blocks(block))
	result = 1;
      Query_cache_block * results = block->query()->result();
      if (results)
      {
	Query_cache_block * result_block = results;
	do
	{
	  DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
				(ulong) block, (uint) block->type));
	  if (in_blocks(result_block))
	    result = 1;

	  result_block = result_block->next;
	} while (result_block != results);
      }
      block = block->next;
    } while (block != queries_blocks);
  }

  DBUG_PRINT("qcache", ("check tables ..."));
4014
  if ((block = tables_blocks))
4015
  {
4016
    do
4017
    {
4018 4019
      DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			    (ulong) block, (uint) block->type));
4020 4021 4022 4023
      size_t length;
      uchar *key = query_cache_table_get_key((uchar*) block, &length, 0);
      uchar* val = hash_search(&tables, key, length);
      if (((uchar*)block) != val)
4024
      {
4025 4026 4027 4028 4029 4030 4031 4032
	DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx",
			     (ulong) block, (ulong) val));
      }
      
      if (in_blocks(block))
	result = 1;
      block=block->next;
    } while (block != tables_blocks);
4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052
  }

  DBUG_PRINT("qcache", ("check free blocks"));
  for (i = 0; i < mem_bin_num; i++)
  {
    if ((block = bins[i].free_blocks))
    {
      uint count = 0;
      do
      {
	DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			      (ulong) block, (uint) block->type));
	if (in_blocks(block))
	  result = 1;
	
	count++;
	block=block->next;
      } while (block != bins[i].free_blocks);
      if (count != bins[i].number)
      {
4053
	DBUG_PRINT("error", ("bins[%d].number= %d, but bin have %d blocks",
4054
			     i, bins[i].number,  count));
4055 4056 4057 4058 4059
	result = 1;
      }
    }
  }
  DBUG_ASSERT(result == 0);
4060
  if (!locked)
4061
    STRUCT_UNLOCK(&structure_guard_mutex);
4062
  DBUG_RETURN(result);
4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080
}


my_bool Query_cache::in_blocks(Query_cache_block * point)
{
  my_bool result = 0;
  Query_cache_block *block = point;
  //back
  do
  {
    if (block->pprev->pnext != block)
    {
      DBUG_PRINT("error",
		 ("block 0x%lx in physical list is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)",
		  (ulong) block, (ulong) block->pprev,
		  (ulong) block->pprev->pnext,
		  (ulong) point));
      //back trace
4081
      for (; block != point; block = block->pnext)
4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108
	    DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block));
      result = 1;
      goto err1;
    }
    block = block->pprev;
  } while (block != first_block && block != point);
  if (block != first_block)
  {
    DBUG_PRINT("error",
	       ("block 0x%lx (0x%lx<-->0x%lx) not owned by pysical list",
		(ulong) block, (ulong) block->pprev, (ulong )block->pnext));
    return 1;
  }

err1:
  //forward
  block = point;
  do
  {
    if (block->pnext->pprev != block)
    {
      DBUG_PRINT("error",
		 ("block 0x%lx in physicel list is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)",
		  (ulong) block, (ulong) block->pnext,
		  (ulong) block->pnext->pprev,
		  (ulong) point));
      //back trace
4109
      for (; block != point; block = block->pprev)
4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137
	    DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block));
      result = 1;
      goto err2;
    }
    block = block->pnext;
  } while (block != first_block);
err2:
  return result;
}


my_bool Query_cache::in_list(Query_cache_block * root,
			     Query_cache_block * point,
			     const char *name)
{
  my_bool result = 0;
  Query_cache_block *block = point;
  //back
  do
  {
    if (block->prev->next != block)
    {
      DBUG_PRINT("error",
		 ("block 0x%lx in list '%s' 0x%lx is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)",
		  (ulong) block, name, (ulong) root, (ulong) block->prev,
		  (ulong) block->prev->next,
		  (ulong) point));
      //back trace
4138
      for (; block != point; block = block->next)
4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177
	    DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block));
      result = 1;
      goto err1;
    }
    block = block->prev;
  } while (block != root && block != point);
  if (block != root)
  {
    DBUG_PRINT("error",
	       ("block 0x%lx (0x%lx<-->0x%lx) not owned by list '%s' 0x%lx",
		(ulong) block, 
		(ulong) block->prev, (ulong) block->next,
		name, (ulong) root));
    return 1;
  }
err1:
  // forward
  block = point;
  do
  {
    if (block->next->prev != block)
    {
      DBUG_PRINT("error",
		 ("block 0x%lx in list '%s' 0x%lx is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)",
		  (ulong) block, name, (ulong) root, (ulong) block->next,
		  (ulong) block->next->prev,
		  (ulong) point));
      //back trace
      for (; block != point; block = block->prev)
	    DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block));
      result = 1;
      goto err2;
    }
    block = block->next;
  } while (block != root);
err2:
  return result;
}

4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211
void dump_node(Query_cache_block_table * node, 
	       const char * call, const char * descr)
{
  DBUG_PRINT("qcache", ("%s: %s: node: 0x%lx", call, descr, (ulong) node));
  DBUG_PRINT("qcache", ("%s: %s: node block: 0x%lx",
			call, descr, (ulong) node->block()));
  DBUG_PRINT("qcache", ("%s: %s: next: 0x%lx", call, descr,
			(ulong) node->next));
  DBUG_PRINT("qcache", ("%s: %s: prev: 0x%lx", call, descr,
			(ulong) node->prev));
}

my_bool Query_cache::in_table_list(Query_cache_block_table * root,
				   Query_cache_block_table * point,
				   const char *name)
{
  my_bool result = 0;
  Query_cache_block_table *table = point;
  dump_node(root, name, "parameter root");
  //back
  do
  {
    dump_node(table, name, "list element << ");
    if (table->prev->next != table)
    {
      DBUG_PRINT("error",
		 ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, prev table 0x%lx(0x%lx) refered as next to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))",
		  (ulong) table, (ulong) table->block(), name, 
		  (ulong) root, (ulong) root->block(),
		  (ulong) table->prev, (ulong) table->prev->block(),
		  (ulong) table->prev->next, 
		  (ulong) table->prev->next->block(),
		  (ulong) point, (ulong) point->block()));
      //back trace
4212
      for (; table != point; table = table->next)
4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258
	    DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)", 
				 (ulong) table, (ulong) table->block()));
      result = 1;
      goto err1;
    }
    table = table->prev;
  } while (table != root && table != point);
  if (table != root)
  {
    DBUG_PRINT("error",
	       ("table 0x%lx(0x%lx) (0x%lx(0x%lx)<-->0x%lx(0x%lx)) not owned by list '%s' 0x%lx(0x%lx)",
		(ulong) table, (ulong) table->block(),
		(ulong) table->prev, (ulong) table->prev->block(),
		(ulong) table->next, (ulong) table->next->block(),
		name, (ulong) root, (ulong) root->block()));
    return 1;
  }
err1:
  // forward
  table = point;
  do
  {
    dump_node(table, name, "list element >> ");
    if (table->next->prev != table)
    {
      DBUG_PRINT("error",
		 ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, next table 0x%lx(0x%lx) refered as prev to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))",
		  (ulong) table, (ulong) table->block(),
		  name, (ulong) root, (ulong) root->block(),
		  (ulong) table->next, (ulong) table->next->block(),
		  (ulong) table->next->prev,
		  (ulong) table->next->prev->block(),
		  (ulong) point, (ulong) point->block()));
      //back trace
      for (; table != point; table = table->prev)
	    DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)",
				 (ulong) table, (ulong) table->block()));
      result = 1;
      goto err2;
    }
    table = table->next;
  } while (table != root);
err2:
  return result;
}

4259
#endif /* DBUG_OFF */
4260 4261

#endif /*HAVE_QUERY_CACHE*/