sql_cache.cc 137 KB
Newer Older
1
/* Copyright (C) 2000 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown 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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown 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.
unknown's avatar
unknown committed
11

unknown's avatar
unknown 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 */

unknown's avatar
unknown committed
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).
unknown's avatar
unknown committed
202

unknown's avatar
unknown committed
203
7. Packing cache.
unknown's avatar
unknown committed
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.
unknown's avatar
unknown committed
270

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
7. Interface
The query cache interfaces with the rest of the server code through 7
functions:
 1. Query_cache::send_result_to_client
       - Called before parsing and used to match a statement with the stored
         queries hash.
         If a match is found the cached result set is sent through repeated
         calls to net_real_write. (note: calling thread doesn't have a regis-
         tered result set writer: thd->net.query_cache_query=0)
 2. Query_cache::store_query
       - Called just before handle_select() and is used to register a result
         set writer to the statement currently being processed
         (thd->net.query_cache_query).
 3. query_cache_insert
       - Called from net_real_write to append a result set to a cached query
         if (and only if) this query has a registered result set writer
         (thd->net.query_cache_query).
 4. Query_cache::invalidate
       - Called from various places to invalidate query cache based on data-
         base, table and myisam file name. During an on going invalidation
         the query cache is temporarily disabled.
 5. Query_cache::flush
       - Used when a RESET QUERY CACHE is issued. This clears the entire
         cache block by block.
 6. Query_cache::resize
       - Used to change the available memory used by the query cache. This
         will also invalidate the entrie query cache in one free operation.
 7. Query_cache::pack
       - Used when a FLUSH QUERY CACHE is issued. This changes the order of
         the used memory blocks in physical memory order and move all avail-
         able memory to the 'bottom' of the memory.


unknown's avatar
unknown committed
304 305 306 307 308 309 310 311 312
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);
unknown's avatar
unknown committed
313 314 315 316 317 318 319 320 321 322
  - 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)

323
    - Another option would be to set thd->lex->safe_to_cache_query to 0
unknown's avatar
unknown committed
324 325 326
      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)
unknown's avatar
unknown committed
327 328
*/

unknown's avatar
unknown committed
329
#include "mysql_priv.h"
unknown's avatar
unknown committed
330
#ifdef HAVE_QUERY_CACHE
unknown's avatar
unknown committed
331 332 333
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
334
#include "../storage/myisammrg/ha_myisammrg.h"
335
#include "../storage/myisammrg/myrg_def.h"
unknown's avatar
unknown committed
336

unknown's avatar
SCRUM  
unknown committed
337 338 339 340
#ifdef EMBEDDED_LIBRARY
#include "emb_qcache.h"
#endif

unknown's avatar
unknown committed
341
#if !defined(EXTRA_DBUG) && !defined(DBUG_OFF)
unknown's avatar
unknown committed
342
#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \
unknown's avatar
unknown committed
343
  pthread_mutex_lock(M);}
unknown's avatar
unknown committed
344
#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\
unknown's avatar
unknown committed
345
  (ulong)(M))); pthread_mutex_unlock(M);}
unknown's avatar
unknown committed
346
#define RW_WLOCK(M) {DBUG_PRINT("lock", ("rwlock wlock 0x%lx",(ulong)(M))); \
unknown's avatar
unknown committed
347
  if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")); \
unknown's avatar
unknown committed
348 349
  else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
#define RW_RLOCK(M) {DBUG_PRINT("lock", ("rwlock rlock 0x%lx", (ulong)(M))); \
unknown's avatar
unknown committed
350
  if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")); \
unknown's avatar
unknown committed
351
  else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); }
unknown's avatar
unknown committed
352
#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \
unknown's avatar
unknown committed
353
  if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
unknown's avatar
unknown committed
354
  else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); }
unknown's avatar
unknown committed
355 356
#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
  pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));}
unknown's avatar
unknown committed
357
#define STRUCT_UNLOCK(M) { \
unknown's avatar
unknown committed
358 359 360
  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",\
unknown's avatar
unknown committed
361 362
  __LINE__,(ulong)(B))); \
  B->query()->lock_writing();}
unknown's avatar
unknown committed
363
#define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\
unknown's avatar
unknown committed
364 365 366
  __LINE__,(ulong)(B))); \
  B->query()->lock_reading();}
#define BLOCK_UNLOCK_WR(B) { \
unknown's avatar
unknown committed
367
  DBUG_PRINT("lock", ("%d UNLOCK_WR 0x%lx",\
unknown's avatar
unknown committed
368 369
  __LINE__,(ulong)(B)));B->query()->unlock_writing();}
#define BLOCK_UNLOCK_RD(B) { \
unknown's avatar
unknown committed
370
  DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\
unknown's avatar
unknown committed
371
  __LINE__,(ulong)(B)));B->query()->unlock_reading();}
unknown's avatar
unknown committed
372 373
#define DUMP(C) DBUG_EXECUTE("qcache", {\
  (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();})
unknown's avatar
unknown committed
374 375 376
#else
#define MUTEX_LOCK(M) pthread_mutex_lock(M)
#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M)
unknown's avatar
unknown committed
377 378 379
#define RW_WLOCK(M) rw_wrlock(M)
#define RW_RLOCK(M) rw_rdlock(M)
#define RW_UNLOCK(M) rw_unlock(M)
unknown's avatar
unknown committed
380 381 382 383 384 385 386 387 388
#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

unknown's avatar
unknown committed
389 390 391
const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS };
TYPELIB query_cache_type_typelib=
{
392
  array_elements(query_cache_type_names)-1,"", query_cache_type_names, NULL
unknown's avatar
unknown committed
393 394
};

unknown's avatar
unknown committed
395
/*****************************************************************************
unknown's avatar
unknown committed
396 397
 Query_cache_block_table method(s)
*****************************************************************************/
unknown's avatar
unknown committed
398 399 400

inline Query_cache_block * Query_cache_block_table::block()
{
401
  return (Query_cache_block *)(((uchar*)this) -
unknown's avatar
unknown committed
402
			       ALIGN_SIZE(sizeof(Query_cache_block_table)*n) -
unknown's avatar
unknown committed
403
			       ALIGN_SIZE(sizeof(Query_cache_block)));
404
}
unknown's avatar
unknown committed
405 406

/*****************************************************************************
unknown's avatar
unknown committed
407 408
   Query_cache_block method(s)
*****************************************************************************/
unknown's avatar
unknown committed
409 410 411 412

void Query_cache_block::init(ulong block_length)
{
  DBUG_ENTER("Query_cache_block::init");
unknown's avatar
unknown committed
413
  DBUG_PRINT("qcache", ("init block: 0x%lx  length: %lu", (ulong) this,
414
			block_length));
unknown's avatar
unknown committed
415 416 417 418 419 420 421 422 423 424
  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");
unknown's avatar
unknown committed
425 426
  DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d",
			(ulong) this, type));
unknown's avatar
unknown committed
427 428 429 430 431 432
  type = INCOMPLETE;
  DBUG_VOID_RETURN;
}

inline uint Query_cache_block::headers_len()
{
unknown's avatar
unknown committed
433
  return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) +
unknown's avatar
unknown committed
434 435 436
	  ALIGN_SIZE(sizeof(Query_cache_block)));
}

437
inline uchar* Query_cache_block::data(void)
unknown's avatar
unknown committed
438
{
439
  return (uchar*)( ((uchar*)this) + headers_len() );
unknown's avatar
unknown committed
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
}

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 *)
473
	  (((uchar*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) +
unknown's avatar
unknown committed
474
	   n*sizeof(Query_cache_block_table)));
unknown's avatar
unknown committed
475 476 477 478 479 480 481
}


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

unknown's avatar
unknown committed
482 483
extern "C"
{
484
uchar *query_cache_table_get_key(const uchar *record, size_t *length,
unknown's avatar
unknown committed
485
				my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
486 487 488 489
{
  Query_cache_block* table_block = (Query_cache_block*) record;
  *length = (table_block->used - table_block->headers_len() -
	     ALIGN_SIZE(sizeof(Query_cache_table)));
490
  return (((uchar *) table_block->data()) +
unknown's avatar
unknown committed
491 492 493 494 495
	  ALIGN_SIZE(sizeof(Query_cache_table)));
}
}

/*****************************************************************************
unknown's avatar
unknown committed
496 497
    Query_cache_query methods
*****************************************************************************/
unknown's avatar
unknown committed
498 499

/*
unknown's avatar
unknown committed
500
   Following methods work for block read/write locking only in this
unknown's avatar
unknown committed
501 502
   particular case and in interaction with structure_guard_mutex.

unknown's avatar
unknown committed
503
   Lock for write prevents any other locking. (exclusive use)
unknown's avatar
unknown committed
504 505 506
   Lock for read prevents only locking for write.
*/

unknown's avatar
unknown committed
507
inline void Query_cache_query::lock_writing()
unknown's avatar
unknown committed
508
{
unknown's avatar
unknown committed
509
  RW_WLOCK(&lock);
unknown's avatar
unknown committed
510 511 512 513 514
}


/*
  Needed for finding queries, that we may delete from cache.
unknown's avatar
unknown committed
515 516 517
  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.
unknown's avatar
unknown committed
518 519 520 521 522
*/

my_bool Query_cache_query::try_lock_writing()
{
  DBUG_ENTER("Query_cache_block::try_lock_writing");
unknown's avatar
unknown committed
523
  if (rw_trywrlock(&lock)!=0)
unknown's avatar
unknown committed
524
  {
unknown's avatar
unknown committed
525
    DBUG_PRINT("info", ("can't lock rwlock"));
unknown's avatar
unknown committed
526 527
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
528
  DBUG_PRINT("info", ("rwlock 0x%lx locked", (ulong) &lock));
unknown's avatar
unknown committed
529 530 531 532
  DBUG_RETURN(1);
}


unknown's avatar
unknown committed
533
inline void Query_cache_query::lock_reading()
unknown's avatar
unknown committed
534
{
unknown's avatar
unknown committed
535
  RW_RLOCK(&lock);
unknown's avatar
unknown committed
536 537 538
}


unknown's avatar
unknown committed
539
inline void Query_cache_query::unlock_writing()
unknown's avatar
unknown committed
540
{
unknown's avatar
unknown committed
541
  RW_UNLOCK(&lock);
unknown's avatar
unknown committed
542 543 544
}


unknown's avatar
unknown committed
545
inline void Query_cache_query::unlock_reading()
unknown's avatar
unknown committed
546
{
unknown's avatar
unknown committed
547
  RW_UNLOCK(&lock);
unknown's avatar
unknown committed
548 549
}

550 551 552 553 554 555 556 557

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",
558
			(long) (((uchar*) this) -
unknown's avatar
unknown committed
559
                                ALIGN_SIZE(sizeof(Query_cache_block)))));
560 561 562 563 564 565 566 567
  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",
568
			(long) (((uchar*) this) -
unknown's avatar
unknown committed
569
                                ALIGN_SIZE(sizeof(Query_cache_block)))));
570 571 572 573 574 575 576 577 578 579
  /*
    The following call is not needed on system where one can destroy an
    active semaphore
  */
  this->unlock_writing();
  rwlock_destroy(&lock);
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
580 581
extern "C"
{
582
uchar *query_cache_query_get_key(const uchar *record, size_t *length,
unknown's avatar
unknown committed
583
				my_bool not_used)
unknown's avatar
unknown committed
584
{
unknown's avatar
unknown committed
585
  Query_cache_block *query_block = (Query_cache_block*) record;
unknown's avatar
unknown committed
586 587
  *length = (query_block->used - query_block->headers_len() -
	     ALIGN_SIZE(sizeof(Query_cache_query)));
588
  return (((uchar *) query_block->data()) +
unknown's avatar
unknown committed
589 590 591 592 593
	  ALIGN_SIZE(sizeof(Query_cache_query)));
}
}

/*****************************************************************************
594
  Functions to store things into the query cache
unknown's avatar
unknown committed
595
*****************************************************************************/
unknown's avatar
unknown committed
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
/*
  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;
}


638 639 640 641
/*
  Insert the packet into the query cache.
*/

unknown's avatar
unknown committed
642 643 644 645
void query_cache_insert(NET *net, const char *packet, ulong length)
{
  DBUG_ENTER("query_cache_insert");

646 647 648 649
  /* See the comment on double-check locking usage above. */
  if (net->query_cache_query == 0)
    DBUG_VOID_RETURN;

650
  STRUCT_LOCK(&query_cache.structure_guard_mutex);
651 652 653 654 655 656 657
  bool interrupt;
  query_cache.wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
  {
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
    return;
  }
658

659 660
  Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
  if (!query_block)
661
  {
662 663 664 665
    /*
      We lost the writer and the currently processed query has been
      invalidated; there is nothing left to do.
    */
666
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
unknown's avatar
unknown committed
667
    DBUG_VOID_RETURN;
668
  }
unknown's avatar
unknown committed
669

670 671
  Query_cache_query *header= query_block->query();
  Query_cache_block *result= header->result();
unknown's avatar
unknown committed
672

673 674 675
  DUMP(&query_cache);
  BLOCK_LOCK_WR(query_block);
  DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
unknown's avatar
unknown committed
676

677 678 679 680 681 682 683 684 685
  /*
    On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we
    still need structure_guard_mutex to free the query, and therefore unlock
    it later in this function.
  */
  if (!query_cache.append_result_data(&result, length, (uchar*) packet,
                                      query_block))
  {
    DBUG_PRINT("warning", ("Can't append data"));
686
    header->result(result);
687 688 689 690
    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
691
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
692 693 694 695 696 697 698 699
    DBUG_VOID_RETURN;
  }

  header->result(result);
  header->last_pkt_nr= net->pkt_nr;
  BLOCK_UNLOCK_WR(query_block);
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););

unknown's avatar
unknown committed
700 701 702
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
703

unknown's avatar
unknown committed
704 705 706 707
void query_cache_abort(NET *net)
{
  DBUG_ENTER("query_cache_abort");

708 709 710 711 712
  /* 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);
713 714 715
  bool interrupt;
  query_cache.wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
unknown's avatar
unknown committed
716
  {
717 718 719
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
    DBUG_VOID_RETURN;
  }
720

721 722 723 724
  /*
    While we were waiting another thread might have changed the status
    of the writer. Make sure the writer still exists before continue.
  */
725 726
  Query_cache_block *query_block= ((Query_cache_block*)
                                   net->query_cache_query);
727
  if (query_block)
728 729 730 731 732 733
  {
    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;
unknown's avatar
unknown committed
734
    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
unknown's avatar
unknown committed
735
  }
736 737 738

  STRUCT_UNLOCK(&query_cache.structure_guard_mutex);

unknown's avatar
unknown committed
739 740 741
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
742

unknown's avatar
SCRUM  
unknown committed
743
void query_cache_end_of_result(THD *thd)
unknown's avatar
unknown committed
744
{
unknown's avatar
unknown committed
745
  Query_cache_block *query_block;
unknown's avatar
unknown committed
746 747
  DBUG_ENTER("query_cache_end_of_result");

748 749 750 751
  /* See the comment on double-check locking usage above. */
  if (thd->net.query_cache_query == 0)
    DBUG_VOID_RETURN;

unknown's avatar
unknown committed
752 753 754 755 756 757
  if (thd->killed)
  {
    query_cache_abort(&thd->net);
    DBUG_VOID_RETURN;
  }

unknown's avatar
SCRUM  
unknown committed
758
#ifdef EMBEDDED_LIBRARY
759 760
  query_cache_insert(&thd->net, (char*)thd, 
                     emb_count_querycache_size(thd));
unknown's avatar
SCRUM  
unknown committed
761
#endif
762

763 764
  STRUCT_LOCK(&query_cache.structure_guard_mutex);

765 766 767 768 769 770 771
  bool interrupt;
  query_cache.wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
  {
    STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
    DBUG_VOID_RETURN;
  }
772

unknown's avatar
unknown committed
773
  query_block= ((Query_cache_block*) thd->net.query_cache_query);
774 775
  if (query_block)
  {
776 777 778 779 780
    /*
      The writer is still present; finish last result block by chopping it to 
      suitable size if needed and setting block type. Since this is the last
      block, the writer should be dropped.
    */
781 782 783
    DUMP(&query_cache);
    BLOCK_LOCK_WR(query_block);
    Query_cache_query *header= query_block->query();
unknown's avatar
unknown committed
784 785 786
    Query_cache_block *last_result_block;
    ulong allign_size;
    ulong len;
unknown's avatar
unknown committed
787

788
    if (header->result() == 0)
unknown's avatar
unknown committed
789
    {
unknown's avatar
unknown committed
790 791
      DBUG_PRINT("error", ("End of data with no result blocks; "
                           "Query '%s' removed from cache.", header->query()));
792
      /*
unknown's avatar
unknown committed
793 794 795
        Extra safety: empty result should not happen in the normal call
        to this function. In the release version that query should be ignored
        and removed from QC.
796
      */
unknown's avatar
unknown committed
797 798
      DBUG_ASSERT(0);
      query_cache.free_query(query_block);
799 800
      STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
      DBUG_VOID_RETURN;
unknown's avatar
unknown committed
801
    }
unknown's avatar
unknown committed
802 803 804 805 806 807 808

    last_result_block= header->result()->prev;
    allign_size= ALIGN_SIZE(last_result_block->used);
    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);

809 810
    header->found_rows(current_thd->limit_found_rows);
    header->result()->type= Query_cache_block::RESULT;
811 812

    /* Drop the writer. */
813 814
    header->writer(0);
    thd->net.query_cache_query= 0;
815

unknown's avatar
unknown committed
816
    BLOCK_UNLOCK_WR(query_block);
817 818
    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););

unknown's avatar
unknown committed
819
  }
820

unknown's avatar
unknown committed
821
  STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
unknown's avatar
unknown committed
822 823 824
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
825
void query_cache_invalidate_by_MyISAM_filename(const char *filename)
unknown's avatar
unknown committed
826 827
{
  query_cache.invalidate_by_MyISAM_filename(filename);
828
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
unknown's avatar
unknown committed
829 830 831
}


832 833 834 835 836 837 838 839 840 841 842 843
/*
  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);
}


unknown's avatar
unknown committed
844
/*****************************************************************************
unknown's avatar
unknown committed
845 846
   Query_cache methods
*****************************************************************************/
unknown's avatar
unknown committed
847

848 849 850 851 852
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)
unknown's avatar
unknown committed
853
  :query_cache_size(0),
854
   query_cache_limit(query_cache_limit_arg),
unknown's avatar
unknown committed
855
   queries_in_cache(0), hits(0), inserts(0), refused(0),
856
   total_blocks(0), lowmem_prunes(0),
857 858 859 860
   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)),
unknown's avatar
unknown committed
861 862
   initialized(0)
{
unknown's avatar
unknown committed
863 864 865
  ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) +
		     ALIGN_SIZE(sizeof(Query_cache_block_table)) +
		     ALIGN_SIZE(sizeof(Query_cache_query)) + 3);
unknown's avatar
unknown committed
866
  set_if_bigger(min_allocation_unit,min_needed);
unknown's avatar
unknown committed
867
  this->min_allocation_unit= ALIGN_SIZE(min_allocation_unit);
unknown's avatar
unknown committed
868
  set_if_bigger(this->min_result_data_size,min_allocation_unit);
unknown's avatar
unknown committed
869 870
}

unknown's avatar
unknown committed
871

unknown's avatar
unknown committed
872
ulong Query_cache::resize(ulong query_cache_size_arg)
unknown's avatar
unknown committed
873
{
874
  ulong new_query_cache_size;
unknown's avatar
unknown committed
875
  DBUG_ENTER("Query_cache::resize");
unknown's avatar
unknown committed
876 877
  DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size,
			query_cache_size_arg));
878
  DBUG_ASSERT(initialized);
879

880
  STRUCT_LOCK(&structure_guard_mutex);
881 882 883
  while (is_flushing())
    pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
  m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
884 885
  STRUCT_UNLOCK(&structure_guard_mutex);

886
  free_cache();
887

unknown's avatar
unknown committed
888
  query_cache_size= query_cache_size_arg;
889
  new_query_cache_size= init_cache();
890 891

  STRUCT_LOCK(&structure_guard_mutex);
892 893
  m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
  pthread_cond_signal(&COND_cache_status_changed);
894 895
  if (new_query_cache_size)
    DBUG_EXECUTE("check_querycache",check_integrity(1););
896
  STRUCT_UNLOCK(&structure_guard_mutex);
897 898

  DBUG_RETURN(new_query_cache_size);
unknown's avatar
unknown committed
899 900
}

unknown's avatar
unknown committed
901

902 903 904 905 906 907 908 909
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));
}


unknown's avatar
unknown committed
910 911
void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
{
912
  TABLE_COUNTER_TYPE local_tables;
913
  ulong tot_length;
unknown's avatar
unknown committed
914
  DBUG_ENTER("Query_cache::store_query");
915 916 917 918 919 920 921 922 923
  /*
    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)
unknown's avatar
unknown committed
924
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
925
  uint8 tables_type= 0;
unknown's avatar
unknown committed
926

927
  if ((local_tables= is_cacheable(thd, thd->query_length,
unknown's avatar
unknown committed
928
				  thd->query, thd->lex, tables_used,
unknown's avatar
unknown committed
929
				  &tables_type)))
unknown's avatar
unknown committed
930
  {
931
    NET *net= &thd->net;
932 933 934
    Query_cache_query_flags flags;
    // fill all gaps between fields with 0 to get repeatable key
    bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
935 936 937
    flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
    flags.client_protocol_41= test(thd->client_capabilities &
                                   CLIENT_PROTOCOL_41);
938 939 940 941 942 943
    /*
      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();
944 945
    flags.more_results_exists= test(thd->server_status &
                                    SERVER_MORE_RESULTS_EXISTS);
946
    flags.pkt_nr= net->pkt_nr;
unknown's avatar
unknown committed
947 948 949
    flags.character_set_client_num=
      thd->variables.character_set_client->number;
    flags.character_set_results_num=
950 951 952
      (thd->variables.character_set_results ?
       thd->variables.character_set_results->number :
       UINT_MAX);
unknown's avatar
unknown committed
953 954
    flags.collation_connection_num=
      thd->variables.collation_connection->number;
955
    flags.limit= thd->variables.select_limit;
956
    flags.time_zone= thd->variables.time_zone;
957 958
    flags.sql_mode= thd->variables.sql_mode;
    flags.max_sort_length= thd->variables.max_sort_length;
959
    flags.lc_time_names= thd->variables.lc_time_names;
960
    flags.group_concat_max_len= thd->variables.group_concat_max_len;
961 962
    flags.div_precision_increment= thd->variables.div_precincrement;
    flags.default_week_format= thd->variables.default_week_format;
963 964
    DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
965
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
966 967
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",                          
968 969
                          (int)flags.client_long_flag,
                          (int)flags.client_protocol_41,
970
                          (int)flags.result_in_binary_protocol,
971
                          (int)flags.more_results_exists,
972
                          flags.pkt_nr,
973 974 975
                          flags.character_set_client_num,
                          flags.character_set_results_num,
                          flags.collation_connection_num,
unknown's avatar
unknown committed
976 977
                          (ulong) flags.limit,
                          (ulong) flags.time_zone,
978 979
                          flags.sql_mode,
                          flags.max_sort_length,
980 981 982
                          flags.group_concat_max_len,
                          flags.div_precision_increment,
                          flags.default_week_format));
983 984 985 986 987
    /*
     Make InnoDB to release the adaptive hash index latch before
     acquiring the query cache mutex.
    */
    ha_release_temporary_latches(thd);
unknown's avatar
unknown committed
988

989
    STRUCT_LOCK(&structure_guard_mutex);
990
    if (query_cache_size == 0 || is_flushing())
991
    {
992 993 994 995 996
      /*
        A table- or a full flush operation can potentially take a long time to 
        finish. We choose not to wait for them and skip caching statements
        instead.
      */
997
      STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
998
      DBUG_VOID_RETURN;
999
    }
unknown's avatar
unknown committed
1000 1001
    DUMP(this);

1002 1003
    if (ask_handler_allowance(thd, tables_used))
    {
unknown's avatar
unknown committed
1004
      refused++;
1005 1006 1007 1008
      STRUCT_UNLOCK(&structure_guard_mutex);
      DBUG_VOID_RETURN;
    }

1009 1010 1011
    /* Key is query + database + flag */
    if (thd->db_length)
    {
1012
      memcpy(thd->query+thd->query_length+1, thd->db, thd->db_length);
unknown's avatar
unknown committed
1013
      DBUG_PRINT("qcache", ("database: %s  length: %u",
1014 1015 1016 1017 1018 1019
			    thd->db, thd->db_length)); 
    }
    else
    {
      DBUG_PRINT("qcache", ("No active database"));
    }
1020 1021
    tot_length= thd->query_length + thd->db_length + 1 +
      QUERY_CACHE_FLAGS_SIZE;
unknown's avatar
unknown committed
1022
    /*
1023 1024
      We should only copy structure (don't use it location directly)
      because of alignment issue
unknown's avatar
unknown committed
1025
    */
1026 1027
    memcpy((void *)(thd->query + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
	   &flags, QUERY_CACHE_FLAGS_SIZE);
unknown's avatar
unknown committed
1028

unknown's avatar
unknown committed
1029
    /* Check if another thread is processing the same query? */
unknown's avatar
unknown committed
1030
    Query_cache_block *competitor = (Query_cache_block *)
1031
      hash_search(&queries, (uchar*) thd->query, tot_length);
1032
    DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor));
unknown's avatar
unknown committed
1033 1034
    if (competitor == 0)
    {
unknown's avatar
unknown committed
1035 1036
      /* Query is not in cache and no one is working with it; Store it */
      Query_cache_block *query_block;
1037
      query_block= write_block_data(tot_length, (uchar*) thd->query,
unknown's avatar
unknown committed
1038
				    ALIGN_SIZE(sizeof(Query_cache_query)),
1039
				    Query_cache_block::QUERY, local_tables);
unknown's avatar
unknown committed
1040 1041
      if (query_block != 0)
      {
unknown's avatar
unknown committed
1042 1043
	DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
			    (ulong) query_block, query_block->used));
unknown's avatar
unknown committed
1044

unknown's avatar
unknown committed
1045
	Query_cache_query *header = query_block->query();
unknown's avatar
unknown committed
1046
	header->init_n_lock();
1047
	if (my_hash_insert(&queries, (uchar*) query_block))
unknown's avatar
unknown committed
1048 1049
	{
	  refused++;
unknown's avatar
unknown committed
1050
	  DBUG_PRINT("qcache", ("insertion in query hash"));
unknown's avatar
unknown committed
1051 1052 1053
	  header->unlock_n_destroy();
	  free_memory_block(query_block);
	  STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1054
	  goto end;
unknown's avatar
unknown committed
1055
	}
1056
	if (!register_all_tables(query_block, tables_used, local_tables))
unknown's avatar
unknown committed
1057 1058
	{
	  refused++;
unknown's avatar
unknown committed
1059
	  DBUG_PRINT("warning", ("tables list including failed"));
1060
	  hash_delete(&queries, (uchar *) query_block);
unknown's avatar
unknown committed
1061 1062 1063
	  header->unlock_n_destroy();
	  free_memory_block(query_block);
	  STRUCT_UNLOCK(&structure_guard_mutex);
1064
	  goto end;
unknown's avatar
unknown committed
1065
	}
unknown's avatar
unknown committed
1066
	double_linked_list_simple_include(query_block, &queries_blocks);
unknown's avatar
unknown committed
1067 1068
	inserts++;
	queries_in_cache++;
1069
	net->query_cache_query= (uchar*) query_block;
unknown's avatar
unknown committed
1070
	header->writer(net);
1071
	header->tables_type(tables_type);
1072 1073 1074

	STRUCT_UNLOCK(&structure_guard_mutex);

unknown's avatar
unknown committed
1075 1076 1077 1078 1079 1080
	// init_n_lock make query block locked
	BLOCK_UNLOCK_WR(query_block);
      }
      else
      {
	// We have not enough memory to store query => do nothing
unknown's avatar
unknown committed
1081
	refused++;
unknown's avatar
unknown committed
1082 1083 1084 1085 1086 1087
	STRUCT_UNLOCK(&structure_guard_mutex);
	DBUG_PRINT("warning", ("Can't allocate query"));
      }
    }
    else
    {
unknown's avatar
unknown committed
1088
      // Another thread is processing the same query => do nothing
unknown's avatar
unknown committed
1089 1090
      refused++;
      STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1091
      DBUG_PRINT("qcache", ("Another thread process same query"));
unknown's avatar
unknown committed
1092 1093
    }
  }
unknown's avatar
unknown committed
1094
  else if (thd->lex->sql_command == SQLCOM_SELECT)
unknown's avatar
unknown committed
1095
    statistic_increment(refused, &structure_guard_mutex);
unknown's avatar
unknown committed
1096 1097

end:
unknown's avatar
unknown committed
1098 1099
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1100

1101

1102 1103 1104 1105 1106 1107 1108 1109 1110
/*
  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.
1111 1112 1113 1114

  NOTE
  This method requires that sql points to allocated memory of size:
  tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
1115
*/
unknown's avatar
unknown committed
1116

1117
int
unknown's avatar
unknown committed
1118 1119
Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
{
unknown's avatar
unknown committed
1120
  ulonglong engine_data;
unknown's avatar
unknown committed
1121 1122 1123
  Query_cache_query *query;
  Query_cache_block *first_result_block, *result_block;
  Query_cache_block_table *block_table, *block_table_end;
1124
  ulong tot_length;
1125
  Query_cache_query_flags flags;
unknown's avatar
unknown committed
1126 1127
  DBUG_ENTER("Query_cache::send_result_to_client");

1128 1129 1130 1131
  /*
    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.
1132

1133 1134 1135 1136 1137
    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;
1138

unknown's avatar
unknown committed
1139
  if (!thd->lex->safe_to_cache_query)
unknown's avatar
unknown committed
1140
  {
unknown's avatar
unknown committed
1141
    DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
unknown's avatar
unknown committed
1142
    goto err;
unknown's avatar
unknown committed
1143 1144 1145
  }

  {
1146 1147 1148 1149 1150 1151 1152
    uint i= 0;
    /*
      Skip '(' characters in queries like following:
      (select a from t1) union (select a from t1);
    */
    while (sql[i]=='(')
      i++;
1153

1154 1155
    /*
      Test if the query is a SELECT
unknown's avatar
unknown committed
1156 1157 1158
      (pre-space is removed in dispatch_command).

      First '/' looks like comment before command it is not
unknown's avatar
unknown committed
1159
      frequently appeared in real life, consequently we can
unknown's avatar
unknown committed
1160
      check all such queries, too.
1161
    */
unknown's avatar
unknown committed
1162
    if ((my_toupper(system_charset_info, sql[i])     != 'S' ||
1163 1164
         my_toupper(system_charset_info, sql[i + 1]) != 'E' ||
         my_toupper(system_charset_info, sql[i + 2]) != 'L') &&
1165
        sql[i] != '/')
1166 1167 1168 1169
    {
      DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
      goto err;
    }
unknown's avatar
unknown committed
1170 1171 1172
  }

  STRUCT_LOCK(&structure_guard_mutex);
1173 1174 1175 1176 1177

  if (query_cache_size == 0)
    goto err_unlock;

  if (is_flushing())
unknown's avatar
unknown committed
1178
  {
1179 1180
    /* Return; Query cache is temporarily disabled while we flush. */
    DBUG_PRINT("qcache",("query cache disabled"));
1181
    goto err_unlock;
unknown's avatar
unknown committed
1182
  }
1183

1184 1185 1186 1187
  /*
    Check that we haven't forgot to reset the query cache variables;
    make sure there are no attached query cache writer to this thread.
   */
1188 1189
  DBUG_ASSERT(thd->net.query_cache_query == 0);

unknown's avatar
unknown committed
1190
  Query_cache_block *query_block;
unknown's avatar
unknown committed
1191

1192
  tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
1193 1194
  if (thd->db_length)
  {
1195
    memcpy(sql+query_length+1, thd->db, thd->db_length);
unknown's avatar
unknown committed
1196
    DBUG_PRINT("qcache", ("database: '%s'  length: %u",
1197 1198 1199 1200 1201 1202
			  thd->db, thd->db_length));
  }
  else
  {
    DBUG_PRINT("qcache", ("No active database"));
  }
1203 1204 1205

  // fill all gaps between fields with 0 to get repeatable key
  bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
1206 1207 1208
  flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
  flags.client_protocol_41= test(thd->client_capabilities &
                                 CLIENT_PROTOCOL_41);
1209
  flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
1210 1211
  flags.more_results_exists= test(thd->server_status &
                                  SERVER_MORE_RESULTS_EXISTS);
1212
  flags.pkt_nr= thd->net.pkt_nr;
unknown's avatar
unknown committed
1213
  flags.character_set_client_num= thd->variables.character_set_client->number;
unknown's avatar
unknown committed
1214
  flags.character_set_results_num=
1215 1216 1217
    (thd->variables.character_set_results ?
     thd->variables.character_set_results->number :
     UINT_MAX);
unknown's avatar
unknown committed
1218
  flags.collation_connection_num= thd->variables.collation_connection->number;
1219
  flags.limit= thd->variables.select_limit;
1220
  flags.time_zone= thd->variables.time_zone;
1221 1222 1223
  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;
1224 1225
  flags.div_precision_increment= thd->variables.div_precincrement;
  flags.default_week_format= thd->variables.default_week_format;
1226
  flags.lc_time_names= thd->variables.lc_time_names;
1227 1228
  DBUG_PRINT("qcache", ("\
long %d, 4.1: %d, bin_proto: %d, more results %d, pkt_nr: %d, \
1229
CS client: %u, CS result: %u, CS conn: %u, limit: %lu, TZ: 0x%lx, \
1230 1231
sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu",                          
1232 1233
                          (int)flags.client_long_flag,
                          (int)flags.client_protocol_41,
1234
                          (int)flags.result_in_binary_protocol,
1235
                          (int)flags.more_results_exists,
1236
                          flags.pkt_nr,
1237 1238 1239
                          flags.character_set_client_num,
                          flags.character_set_results_num,
                          flags.collation_connection_num,
unknown's avatar
unknown committed
1240 1241
                          (ulong) flags.limit,
                          (ulong) flags.time_zone,
1242 1243
                          flags.sql_mode,
                          flags.max_sort_length,
1244 1245 1246
                          flags.group_concat_max_len,
                          flags.div_precision_increment,
                          flags.default_week_format));
1247 1248 1249
  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,
1250
						   tot_length);
unknown's avatar
unknown committed
1251
  /* Quick abort on unlocked data */
unknown's avatar
unknown committed
1252 1253 1254 1255
  if (query_block == 0 ||
      query_block->query()->result() == 0 ||
      query_block->query()->result()->type != Query_cache_block::RESULT)
  {
unknown's avatar
unknown committed
1256
    DBUG_PRINT("qcache", ("No query in query hash or no results"));
1257
    goto err_unlock;
unknown's avatar
unknown committed
1258
  }
unknown's avatar
unknown committed
1259
  DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block));
unknown's avatar
unknown committed
1260

unknown's avatar
unknown committed
1261
  /* Now lock and test that nothing changed while blocks was unlocked */
unknown's avatar
unknown committed
1262 1263
  BLOCK_LOCK_RD(query_block);

unknown's avatar
unknown committed
1264 1265 1266 1267 1268 1269 1270
  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"));
unknown's avatar
unknown committed
1271
    BLOCK_UNLOCK_RD(query_block);
1272
    goto err_unlock;
unknown's avatar
unknown committed
1273
  }
unknown's avatar
unknown committed
1274 1275
  DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query));

1276 1277 1278 1279 1280 1281 1282 1283 1284
  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;
  }
      
unknown's avatar
unknown committed
1285 1286 1287
  // Check access;
  block_table= query_block->table(0);
  block_table_end= block_table+query_block->n_tables;
1288
  for (; block_table != block_table_end; block_table++)
unknown's avatar
unknown committed
1289 1290
  {
    TABLE_LIST table_list;
1291
    TABLE *tmptable;
unknown's avatar
unknown committed
1292
    Query_cache_table *table = block_table->parent;
1293 1294 1295 1296 1297 1298 1299 1300 1301

    /*
      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)
    {
unknown's avatar
unknown committed
1302
      if (tmptable->s->table_cache_key.length - TMP_TABLE_KEY_EXTRA == 
1303
          table->key_length() &&
unknown's avatar
unknown committed
1304
          !memcmp(tmptable->s->table_cache_key.str, table->data(),
unknown's avatar
unknown committed
1305
                  table->key_length()))
1306 1307 1308 1309 1310 1311 1312
      {
        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
unknown's avatar
unknown committed
1313
          temporary tables => assign following variable to make check
1314 1315
          faster.
        */
unknown's avatar
unknown committed
1316
        thd->lex->safe_to_cache_query=0;
1317 1318 1319 1320 1321 1322
        BLOCK_UNLOCK_RD(query_block);
        DBUG_RETURN(-1);
      }
    }

    bzero((char*) &table_list,sizeof(table_list));
unknown's avatar
unknown committed
1323
    table_list.db = table->db();
1324
    table_list.alias= table_list.table_name= table->table();
unknown's avatar
unknown committed
1325
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1326
    if (check_table_access(thd,SELECT_ACL,&table_list,1))
unknown's avatar
unknown committed
1327 1328 1329
    {
      DBUG_PRINT("qcache",
		 ("probably no SELECT access to %s.%s =>  return to normal processing",
1330
		  table_list.db, table_list.alias));
unknown's avatar
unknown committed
1331
      STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1332
      thd->lex->safe_to_cache_query=0;		// Don't try to cache this
1333 1334 1335 1336 1337 1338
      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",
1339
			    table_list.db, table_list.alias));
1340
      BLOCK_UNLOCK_RD(query_block);
unknown's avatar
unknown committed
1341
      thd->lex->safe_to_cache_query= 0;		// Don't try to cache this
1342
      goto err_unlock;				// Parse query
unknown's avatar
unknown committed
1343
    }
unknown's avatar
unknown committed
1344
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
unknown's avatar
unknown committed
1345 1346 1347 1348 1349
    engine_data= table->engine_data();
    if (table->callback() &&
        !(*table->callback())(thd, table->db(),
                              table->key_length(),
                              &engine_data))
1350
    {
1351
      DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
1352 1353
			    table_list.db, table_list.alias));
      BLOCK_UNLOCK_RD(query_block);
unknown's avatar
unknown committed
1354 1355 1356
      if (engine_data != table->engine_data())
      {
        DBUG_PRINT("qcache",
unknown's avatar
unknown committed
1357 1358 1359
                   ("Handler require invalidation queries of %s.%s %lu-%lu",
                    table_list.db, table_list.alias,
                    (ulong) engine_data, (ulong) table->engine_data()));
1360 1361 1362
        invalidate_table_internal(thd,
                                  (uchar *) table->db(),
                                  table->key_length());
unknown's avatar
unknown committed
1363
      }
unknown's avatar
unknown committed
1364 1365
      else
        thd->lex->safe_to_cache_query= 0;       // Don't try to cache this
1366
      goto err_unlock;				// Parse query
unknown's avatar
unknown committed
1367
    }
1368
    else
unknown's avatar
unknown committed
1369 1370
      DBUG_PRINT("qcache", ("handler allow caching %s,%s",
			    table_list.db, table_list.alias));
unknown's avatar
unknown committed
1371
  }
unknown's avatar
unknown committed
1372 1373
  move_to_query_list_end(query_block);
  hits++;
unknown's avatar
unknown committed
1374 1375
  STRUCT_UNLOCK(&structure_guard_mutex);

unknown's avatar
unknown committed
1376 1377 1378
  /*
    Send cached result to client
  */
unknown's avatar
SCRUM  
unknown committed
1379
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1380 1381
  do
  {
unknown's avatar
unknown committed
1382
    DBUG_PRINT("qcache", ("Results  (len: %lu  used: %lu  headers: %lu)",
unknown's avatar
SCRUM  
unknown committed
1383
			  result_block->length, result_block->used,
unknown's avatar
unknown committed
1384 1385
			  (ulong) (result_block->headers_len()+
                                   ALIGN_SIZE(sizeof(Query_cache_result)))));
unknown's avatar
SCRUM  
unknown committed
1386
    
unknown's avatar
unknown committed
1387 1388 1389 1390 1391 1392
    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
unknown's avatar
unknown committed
1393
    result_block = result_block->next;
1394
    thd->net.pkt_nr= query->last_pkt_nr; // Keep packet number updated
unknown's avatar
unknown committed
1395
  } while (result_block != first_result_block);
unknown's avatar
SCRUM  
unknown committed
1396 1397 1398 1399 1400 1401 1402
#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*/
unknown's avatar
unknown committed
1403 1404

  thd->limit_found_rows = query->found_rows();
1405
  thd->status_var.last_query_cost= 0.0;
unknown's avatar
unknown committed
1406 1407

  BLOCK_UNLOCK_RD(query_block);
1408
  DBUG_RETURN(1);				// Result sent to client
unknown's avatar
unknown committed
1409

1410 1411
err_unlock:
  STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1412
err:
1413
  DBUG_RETURN(0);				// Query was not cached
unknown's avatar
unknown committed
1414 1415
}

1416

unknown's avatar
unknown committed
1417 1418 1419 1420
/*
  Remove all cached queries that uses any of the tables in the list
*/

1421 1422
void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
			     my_bool using_transactions)
unknown's avatar
unknown committed
1423 1424
{
  DBUG_ENTER("Query_cache::invalidate (table list)");
1425

1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
  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(thd, tables_used);
unknown's avatar
unknown committed
1444
  }
1445

unknown's avatar
unknown committed
1446 1447 1448
  DBUG_VOID_RETURN;
}

1449
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
unknown's avatar
unknown committed
1450
{
1451
  DBUG_ENTER("Query_cache::invalidate (changed table list)");
1452 1453
  THD *thd= current_thd;
  for (; tables_used; tables_used= tables_used->next)
unknown's avatar
unknown committed
1454
  {
1455 1456 1457 1458
    invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
    DBUG_PRINT("qcache", ("db: %s  table: %s", tables_used->key,
                          tables_used->key+
                          strlen(tables_used->key)+1));
unknown's avatar
unknown committed
1459 1460
  }
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1461 1462
}

1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475

/*
  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)
{
1476
  DBUG_ENTER("Query_cache::invalidate_locked_for_write");
1477
  for (; tables_used; tables_used= tables_used->next_local)
1478
  {
1479 1480
    if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
        tables_used->table)
1481
    {
1482 1483
      THD *thd= current_thd; 
      invalidate_table(thd, tables_used->table);
1484 1485 1486 1487 1488
    }
  }
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1489
/*
1490
  Remove all cached queries that uses the given table
unknown's avatar
unknown committed
1491 1492
*/

1493 1494
void Query_cache::invalidate(THD *thd, TABLE *table, 
			     my_bool using_transactions)
unknown's avatar
unknown committed
1495
{
1496 1497
  DBUG_ENTER("Query_cache::invalidate (table)");
  
1498 1499 1500 1501 1502 1503 1504 1505
  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(thd, table);

1506

unknown's avatar
unknown committed
1507
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1508 1509
}

unknown's avatar
unknown committed
1510 1511 1512 1513
void Query_cache::invalidate(THD *thd, const char *key, uint32  key_length,
			     my_bool using_transactions)
{
  DBUG_ENTER("Query_cache::invalidate (key)");
1514 1515 1516 1517 1518 1519 1520

  using_transactions= using_transactions &&
    (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
    invalidate_table(thd, (uchar*)key, key_length);
1521

unknown's avatar
unknown committed
1522 1523 1524
  DBUG_VOID_RETURN;
}

1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565

/**
  @brief Synchronize the thread with any flushing operations.

  This helper function is called whenever a thread needs to operate on the
  query cache structure (example: during invalidation). If a table flush is in
  progress this function will wait for it to stop. If a full flush is in
  progress, the function will set the interrupt parameter to indicate that the
  current operation is redundant and should be interrupted.

  @param[out] interrupt This out-parameter will be set to TRUE if the calling
    function is redundant and should be interrupted.

  @return If the interrupt-parameter is TRUE then m_cache_status is set to
    NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then
    m_cache_status is set to FLUSH_IN_PROGRESS.
    The structure_guard_mutex will in any case be locked.
*/

void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt)
{
  while (is_flushing())
  {
    /*
      If there already is a full flush in progress query cache isn't enabled
      and additional flushes are redundant; just return instead.
    */
    if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS)
    {
      *interrupt= TRUE;
      return;
    }
    /*
      If a table flush is in progress; wait on cache status to change.
    */
    if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS)
      pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
  }
  *interrupt= FALSE;
}

1566

1567 1568
/**
   @brief Remove all cached queries that uses the given database
unknown's avatar
unknown committed
1569
*/
1570

unknown's avatar
unknown committed
1571
void Query_cache::invalidate(char *db)
unknown's avatar
unknown committed
1572
{
1573
  bool restart= FALSE;
unknown's avatar
unknown committed
1574
  DBUG_ENTER("Query_cache::invalidate (db)");
1575

1576
  STRUCT_LOCK(&structure_guard_mutex);
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
  bool interrupt;
  wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    return;
  }

  THD *thd= current_thd;

  if (query_cache_size > 0)
unknown's avatar
unknown committed
1588
  {
1589 1590
    if (tables_blocks)
    {
1591 1592 1593 1594 1595 1596 1597 1598
      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)
1599 1600 1601 1602
          {
            Query_cache_block_table *list_root= table_block->table(0);
            invalidate_query_block_list(thd,list_root);
          }
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628

          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);
1629
        /*
1630 1631 1632 1633 1634
           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.
1635
        */
1636 1637
      } while (restart);
    } // end if( tables_blocks )
unknown's avatar
unknown committed
1638
  }
1639 1640
  STRUCT_UNLOCK(&structure_guard_mutex);

unknown's avatar
unknown committed
1641
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1642 1643
}

unknown's avatar
unknown committed
1644 1645

void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
unknown's avatar
unknown committed
1646 1647
{
  DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
1648

1649 1650 1651 1652 1653 1654
  /* Calculate the key outside the lock to make the lock shorter */
  char key[MAX_DBKEY_LENGTH];
  uint32 db_length;
  uint key_length= filename_2_table_key(key, filename, &db_length);
  THD *thd= current_thd;
  invalidate_table(thd,(uchar *)key, key_length);
unknown's avatar
unknown committed
1655 1656
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1657

unknown's avatar
unknown committed
1658
  /* Remove all queries from cache */
unknown's avatar
unknown committed
1659

unknown's avatar
unknown committed
1660
void Query_cache::flush()
unknown's avatar
unknown committed
1661
{
unknown's avatar
unknown committed
1662
  DBUG_ENTER("Query_cache::flush");
unknown's avatar
unknown committed
1663
  STRUCT_LOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1664 1665 1666 1667 1668 1669
  if (query_cache_size > 0)
  {
    DUMP(this);
    flush_cache();
    DUMP(this);
  }
unknown's avatar
unknown committed
1670 1671

  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
unknown's avatar
unknown committed
1672
  STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1673
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1674 1675
}

1676 1677 1678 1679 1680 1681 1682 1683 1684 1685

/**
  @brief Rearrange the memory blocks and join result in cache in 1 block (if
    result length > join_limit)

  @param[in] join_limit If the minimum length of a result block to be joined.
  @param[in] iteration_limit The maximum number of packing and joining
    sequences.

*/
unknown's avatar
unknown committed
1686

unknown's avatar
unknown committed
1687 1688 1689
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
  DBUG_ENTER("Query_cache::pack");
1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705

  bool interrupt;
  STRUCT_LOCK(&structure_guard_mutex);
  wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    DBUG_VOID_RETURN;
  }

  if (query_cache_size == 0)
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    DBUG_VOID_RETURN;
  }

unknown's avatar
unknown committed
1706 1707 1708 1709 1710
  uint i = 0;
  do
  {
    pack_cache();
  } while ((++i < iteration_limit) && join_results(join_limit));
1711 1712

  STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
1713 1714
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1715

unknown's avatar
unknown committed
1716

unknown's avatar
unknown committed
1717
void Query_cache::destroy()
unknown's avatar
unknown committed
1718
{
1719 1720
  DBUG_ENTER("Query_cache::destroy");
  if (!initialized)
unknown's avatar
unknown committed
1721 1722 1723
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
  }
1724 1725
  else
  {
1726 1727
    /* Underlying code expects the lock. */
    STRUCT_LOCK(&structure_guard_mutex);
1728
    free_cache();
1729 1730
    STRUCT_UNLOCK(&structure_guard_mutex);

1731
    pthread_cond_destroy(&COND_cache_status_changed);
1732 1733 1734
    pthread_mutex_destroy(&structure_guard_mutex);
    initialized = 0;
  }
unknown's avatar
unknown committed
1735 1736
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1737

unknown's avatar
unknown committed
1738 1739

/*****************************************************************************
unknown's avatar
unknown committed
1740 1741
  init/destroy
*****************************************************************************/
unknown's avatar
unknown committed
1742 1743 1744 1745 1746

void Query_cache::init()
{
  DBUG_ENTER("Query_cache::init");
  pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
1747 1748
  pthread_cond_init(&COND_cache_status_changed, NULL);
  m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
unknown's avatar
unknown committed
1749 1750 1751 1752
  initialized = 1;
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1753

unknown's avatar
unknown committed
1754 1755
ulong Query_cache::init_cache()
{
unknown's avatar
unknown committed
1756 1757 1758
  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;
unknown's avatar
unknown committed
1759
  int align;
unknown's avatar
unknown committed
1760

unknown's avatar
unknown committed
1761
  DBUG_ENTER("Query_cache::init_cache");
1762

unknown's avatar
unknown committed
1763
  approx_additional_data_size = (sizeof(Query_cache) +
1764
				 sizeof(uchar*)*(def_query_hash_size+
1765
					       def_table_hash_size));
unknown's avatar
unknown committed
1766
  if (query_cache_size < approx_additional_data_size)
unknown's avatar
unknown committed
1767
    goto err;
unknown's avatar
unknown committed
1768

unknown's avatar
unknown committed
1769 1770
  query_cache_size-= approx_additional_data_size;
  align= query_cache_size % ALIGN_SIZE(1);
1771 1772 1773 1774 1775
  if (align)
  {
    query_cache_size-= align;
    approx_additional_data_size+= align;
  }
unknown's avatar
unknown committed
1776

unknown's avatar
unknown committed
1777 1778 1779 1780
  /*
    Count memory bins number.
    Check section 6. in start comment for the used algorithm.
  */
unknown's avatar
unknown committed
1781

unknown's avatar
unknown committed
1782 1783 1784 1785 1786 1787 1788
  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;
1789 1790 1791 1792 1793 1794
  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;
  }
unknown's avatar
unknown committed
1795 1796 1797 1798 1799
  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;
unknown's avatar
unknown committed
1800
    mem_bin_steps++;
unknown's avatar
unknown committed
1801 1802 1803 1804 1805 1806
    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);
unknown's avatar
unknown committed
1807
  }
unknown's avatar
unknown committed
1808 1809 1810 1811 1812 1813 1814 1815
  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))));

unknown's avatar
unknown committed
1816
  if (query_cache_size < additional_data_size)
unknown's avatar
unknown committed
1817 1818
    goto err;
  query_cache_size -= additional_data_size;
unknown's avatar
unknown committed
1819

1820
  if (!(cache= (uchar *)
1821
        my_malloc_lock(query_cache_size+additional_data_size, MYF(0))))
unknown's avatar
unknown committed
1822
    goto err;
unknown's avatar
unknown committed
1823

unknown's avatar
unknown committed
1824 1825
  DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins",
		      query_cache_size, min_allocation_unit, mem_bin_num));
unknown's avatar
unknown committed
1826

unknown's avatar
unknown committed
1827 1828 1829 1830
  steps = (Query_cache_memory_bin_step *) cache;
  bins = ((Query_cache_memory_bin *)
	  (cache + mem_bin_steps *
	   ALIGN_SIZE(sizeof(Query_cache_memory_bin_step))));
unknown's avatar
unknown committed
1831

unknown's avatar
unknown committed
1832 1833
  first_block = (Query_cache_block *) (cache + additional_data_size);
  first_block->init(query_cache_size);
unknown's avatar
unknown committed
1834
  total_blocks++;
unknown's avatar
unknown committed
1835 1836
  first_block->pnext=first_block->pprev=first_block;
  first_block->next=first_block->prev=first_block;
unknown's avatar
unknown committed
1837

unknown's avatar
unknown committed
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853
  /* 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;
unknown's avatar
unknown committed
1854
    }
unknown's avatar
unknown committed
1855 1856 1857 1858 1859 1860 1861 1862
    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);
unknown's avatar
unknown committed
1863
  }
unknown's avatar
unknown committed
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881
  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;
    }
  }
unknown's avatar
unknown committed
1882 1883
  bins[mem_bin_num].number = 1;	// For easy end test in get_free_block
  free_memory = free_memory_blocks = 0;
unknown's avatar
unknown committed
1884 1885 1886 1887
  insert_into_free_memory_list(first_block);

  DUMP(this);

1888
  VOID(hash_init(&queries, &my_charset_bin, def_query_hash_size, 0, 0,
unknown's avatar
unknown committed
1889
		 query_cache_query_get_key, 0, 0));
1890
#ifndef FN_NO_CASE_SENCE
1891 1892 1893 1894 1895 1896 1897 1898
  /*
    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.
  */
1899
  VOID(hash_init(&tables, &my_charset_bin, def_table_hash_size, 0, 0,
unknown's avatar
unknown committed
1900
		 query_cache_table_get_key, 0, 0));
unknown's avatar
unknown committed
1901
#else
1902
  /*
unknown's avatar
unknown committed
1903 1904 1905 1906 1907
    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
1908 1909
    comparison.
  */
unknown's avatar
unknown committed
1910
  VOID(hash_init(&tables,
1911
		 lower_case_table_names ? &my_charset_bin :
1912
		 files_charset_info,
unknown's avatar
unknown committed
1913
		 def_table_hash_size, 0, 0,query_cache_table_get_key, 0, 0));
unknown's avatar
unknown committed
1914
#endif
unknown's avatar
unknown committed
1915

unknown's avatar
unknown committed
1916 1917 1918 1919
  queries_in_cache = 0;
  queries_blocks = 0;
  DBUG_RETURN(query_cache_size +
	      additional_data_size + approx_additional_data_size);
unknown's avatar
unknown committed
1920 1921 1922 1923

err:
  make_disabled();
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1924 1925
}

unknown's avatar
unknown committed
1926 1927 1928

/* Disable the use of the query cache */

unknown's avatar
unknown committed
1929 1930 1931
void Query_cache::make_disabled()
{
  DBUG_ENTER("Query_cache::make_disabled");
unknown's avatar
unknown committed
1932
  query_cache_size= 0;
1933
  queries_blocks= 0;
unknown's avatar
unknown committed
1934 1935 1936 1937 1938 1939 1940
  free_memory= 0;
  bins= 0;
  steps= 0;
  cache= 0;
  mem_bin_num= mem_bin_steps= 0;
  queries_in_cache= 0;
  first_block= 0;
1941 1942
  total_blocks= 0;
  tables_blocks= 0;
unknown's avatar
unknown committed
1943 1944 1945
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
1946

1947 1948 1949
/**
  @class Query_cache
  @brief Free all resources allocated by the cache.
1950 1951 1952 1953

  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.
1954 1955
*/

1956
void Query_cache::free_cache()
unknown's avatar
unknown committed
1957 1958 1959
{
  DBUG_ENTER("Query_cache::free_cache");

1960
  my_free((uchar*) cache, MYF(MY_ALLOW_ZERO_PTR));
1961 1962 1963
  make_disabled();
  hash_free(&queries);
  hash_free(&tables);
unknown's avatar
unknown committed
1964 1965 1966 1967
  DBUG_VOID_RETURN;
}

/*****************************************************************************
unknown's avatar
unknown committed
1968
  Free block data
unknown's avatar
unknown committed
1969 1970
*****************************************************************************/

1971

1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982
/**
  @brief Flush the cache.

  This function will flush cache contents.  It assumes we have
  'structure_guard_mutex' locked. The function sets the m_cache_status flag and
  releases the lock, so other threads may proceed skipping the cache as if it
  is disabled. Concurrent flushes are performed in turn.
  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.
unknown's avatar
unknown committed
1983
*/
unknown's avatar
unknown committed
1984 1985 1986

void Query_cache::flush_cache()
{
1987 1988 1989 1990 1991 1992 1993
  /*
    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.
  */
1994 1995
  while (is_flushing())
    pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
1996 1997

  /*
1998
    Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
1999 2000 2001
    the cache while we are in the middle of the flush, and we release
    the lock so that other threads won't block.
  */
2002
  m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
2003 2004 2005
  STRUCT_UNLOCK(&structure_guard_mutex);

  my_hash_reset(&queries);
unknown's avatar
unknown committed
2006 2007
  while (queries_blocks != 0)
  {
unknown's avatar
unknown committed
2008
    BLOCK_LOCK_WR(queries_blocks);
2009
    free_query_internal(queries_blocks);
unknown's avatar
unknown committed
2010
  }
2011 2012

  STRUCT_LOCK(&structure_guard_mutex);
2013 2014
  m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
  pthread_cond_signal(&COND_cache_status_changed);
unknown's avatar
unknown committed
2015 2016
}

unknown's avatar
unknown committed
2017 2018 2019 2020 2021
/*
  Free oldest query that is not in use by another thread.
  Returns 1 if we couldn't remove anything
*/

unknown's avatar
unknown committed
2022 2023 2024
my_bool Query_cache::free_old_query()
{
  DBUG_ENTER("Query_cache::free_old_query");
unknown's avatar
unknown committed
2025
  if (queries_blocks)
unknown's avatar
unknown committed
2026
  {
unknown's avatar
unknown committed
2027 2028 2029 2030 2031
    /*
      try_lock_writing used to prevent client because here lock
      sequence is breached.
      Also we don't need remove locked queries at this point.
    */
2032
    Query_cache_block *query_block= 0;
unknown's avatar
unknown committed
2033
    if (queries_blocks != 0)
unknown's avatar
unknown committed
2034
    {
unknown's avatar
unknown committed
2035 2036 2037
      Query_cache_block *block = queries_blocks;
      /* Search until we find first query that we can remove */
      do
unknown's avatar
unknown committed
2038
      {
unknown's avatar
unknown committed
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048
	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 );
    }
unknown's avatar
unknown committed
2049

unknown's avatar
unknown committed
2050 2051 2052
    if (query_block != 0)
    {
      free_query(query_block);
2053
      lowmem_prunes++;
unknown's avatar
unknown committed
2054 2055
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
2056
  }
unknown's avatar
unknown committed
2057
  DBUG_RETURN(1);				// Nothing to remove
unknown's avatar
unknown committed
2058 2059
}

2060

unknown's avatar
unknown committed
2061
/*
2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076
  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.
unknown's avatar
unknown committed
2077 2078
*/

2079
void Query_cache::free_query_internal(Query_cache_block *query_block)
unknown's avatar
unknown committed
2080
{
2081
  DBUG_ENTER("Query_cache::free_query_internal");
unknown's avatar
unknown committed
2082 2083
  DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
		      (ulong) query_block,
unknown's avatar
unknown committed
2084
		      query_block->query()->length() ));
unknown's avatar
unknown committed
2085

unknown's avatar
unknown committed
2086 2087
  queries_in_cache--;

2088
  Query_cache_query *query= query_block->query();
unknown's avatar
unknown committed
2089

unknown's avatar
unknown committed
2090 2091
  if (query->writer() != 0)
  {
unknown's avatar
unknown committed
2092
    /* Tell MySQL that this query should not be cached anymore */
2093
    query->writer()->query_cache_query= 0;
unknown's avatar
unknown committed
2094 2095
    query->writer(0);
  }
unknown's avatar
unknown committed
2096
  double_linked_list_exclude(query_block, &queries_blocks);
2097
  Query_cache_block_table *table= query_block->table(0);
unknown's avatar
unknown committed
2098

2099
  for (TABLE_COUNTER_TYPE i= 0; i < query_block->n_tables; i++)
unknown's avatar
unknown committed
2100
    unlink_table(table++);
2101
  Query_cache_block *result_block= query->result();
unknown's avatar
unknown committed
2102 2103 2104 2105 2106

  /*
    The following is true when query destruction was called and no results
    in query . (query just registered and then abort/pack/flush called)
  */
unknown's avatar
unknown committed
2107 2108
  if (result_block != 0)
  {
2109 2110 2111 2112 2113 2114
    if (result_block->type != Query_cache_block::RESULT)
    {
      // removing unfinished query
      refused++;
      inserts--;
    }
2115
    Query_cache_block *block= result_block;
unknown's avatar
unknown committed
2116 2117
    do
    {
2118 2119
      Query_cache_block *current= block;
      block= block->next;
unknown's avatar
unknown committed
2120 2121 2122
      free_memory_block(current);
    } while (block != result_block);
  }
2123 2124 2125 2126 2127 2128
  else
  {
    // removing unfinished query
    refused++;
    inserts--;
  }
unknown's avatar
unknown committed
2129 2130 2131 2132 2133 2134 2135

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

  DBUG_VOID_RETURN;
}

2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155

/*
  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() ));

2156
  hash_delete(&queries,(uchar *) query_block);
2157 2158 2159 2160 2161
  free_query_internal(query_block);

  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
2162
/*****************************************************************************
unknown's avatar
unknown committed
2163 2164
 Query data creation
*****************************************************************************/
unknown's avatar
unknown committed
2165 2166

Query_cache_block *
2167
Query_cache::write_block_data(ulong data_len, uchar* data,
unknown's avatar
unknown committed
2168 2169
			      ulong header_len,
			      Query_cache_block::block_type type,
2170
			      TABLE_COUNTER_TYPE ntab)
unknown's avatar
unknown committed
2171
{
unknown's avatar
unknown committed
2172 2173 2174 2175
  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;
2176
  ulong align_len= ALIGN_SIZE(len);
unknown's avatar
unknown committed
2177
  DBUG_ENTER("Query_cache::write_block_data");
unknown's avatar
unknown committed
2178
  DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
unknown's avatar
unknown committed
2179
		      data_len, header_len, all_headers_len));
2180 2181
  Query_cache_block *block= allocate_block(max(align_len,
                                           min_allocation_unit),1, 0);
unknown's avatar
unknown committed
2182 2183 2184 2185 2186 2187
  if (block != 0)
  {
    block->type = type;
    block->n_tables = ntab;
    block->used = len;

2188
    memcpy((uchar *) block+ all_headers_len, data, data_len);
unknown's avatar
unknown committed
2189 2190 2191 2192
  }
  DBUG_RETURN(block);
}

unknown's avatar
unknown committed
2193 2194 2195 2196 2197

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

unknown's avatar
unknown committed
2198
my_bool
unknown's avatar
unknown committed
2199
Query_cache::append_result_data(Query_cache_block **current_block,
2200
				ulong data_len, uchar* data,
unknown's avatar
unknown committed
2201
				Query_cache_block *query_block)
unknown's avatar
unknown committed
2202
{
unknown's avatar
unknown committed
2203 2204
  DBUG_ENTER("Query_cache::append_result_data");
  DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query",
unknown's avatar
unknown committed
2205
		      data_len, (long) query_block));
unknown's avatar
unknown committed
2206

unknown's avatar
unknown committed
2207 2208
  if (query_block->query()->add(data_len) > query_cache_limit)
  {
unknown's avatar
unknown committed
2209
    DBUG_PRINT("qcache", ("size limit reached %lu > %lu",
unknown's avatar
unknown committed
2210 2211 2212 2213
			query_block->query()->length(),
			query_cache_limit));
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
2214
  if (*current_block == 0)
unknown's avatar
unknown committed
2215
  {
unknown's avatar
unknown committed
2216
    DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len));
unknown's avatar
unknown committed
2217
    /*
unknown's avatar
unknown committed
2218 2219
      STRUCT_UNLOCK(&structure_guard_mutex) Will be done by
      write_result_data if success;
unknown's avatar
unknown committed
2220
    */
unknown's avatar
unknown committed
2221
    DBUG_RETURN(write_result_data(current_block, data_len, data, query_block,
unknown's avatar
unknown committed
2222 2223
				  Query_cache_block::RES_BEG));
  }
unknown's avatar
unknown committed
2224
  Query_cache_block *last_block = (*current_block)->prev;
unknown's avatar
unknown committed
2225

unknown's avatar
unknown committed
2226 2227
  DBUG_PRINT("qcache", ("lastblock 0x%lx len %lu used %lu",
		      (ulong) last_block, last_block->length,
unknown's avatar
unknown committed
2228 2229
		      last_block->used));
  my_bool success = 1;
unknown's avatar
unknown committed
2230
  ulong last_block_free_space= last_block->length - last_block->used;
unknown's avatar
unknown committed
2231

unknown's avatar
unknown committed
2232 2233 2234 2235 2236
  /*
    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.
  */
unknown's avatar
unknown committed
2237

unknown's avatar
unknown committed
2238
  // Try join blocks if physically next block is free...
2239 2240
  ulong tail = data_len - last_block_free_space;
  ulong append_min = get_min_append_result_data_size();
unknown's avatar
unknown committed
2241 2242
  if (last_block_free_space < data_len &&
      append_next_free_block(last_block,
2243
			     max(tail, append_min)))
unknown's avatar
unknown committed
2244
    last_block_free_space = last_block->length - last_block->used;
unknown's avatar
unknown committed
2245
  // If no space in last block (even after join) allocate new block
unknown's avatar
unknown committed
2246 2247
  if (last_block_free_space < data_len)
  {
unknown's avatar
unknown committed
2248
    DBUG_PRINT("qcache", ("allocate new block for %lu bytes",
unknown's avatar
unknown committed
2249 2250 2251
			data_len-last_block_free_space));
    Query_cache_block *new_block = 0;
    /*
unknown's avatar
unknown committed
2252 2253
      On success STRUCT_UNLOCK(&structure_guard_mutex) will be done
      by the next call
unknown's avatar
unknown committed
2254
    */
unknown's avatar
unknown committed
2255
    success = write_result_data(&new_block, data_len-last_block_free_space,
2256
				(uchar*)(((uchar*)data)+last_block_free_space),
unknown's avatar
unknown committed
2257 2258 2259
				query_block,
				Query_cache_block::RES_CONT);
    /*
unknown's avatar
unknown committed
2260 2261
       new_block may be != 0 even !success (if write_result_data
       allocate a small block but failed to allocate continue)
unknown's avatar
unknown committed
2262 2263 2264 2265 2266
    */
    if (new_block != 0)
      double_linked_list_join(last_block, new_block);
  }
  else
unknown's avatar
unknown committed
2267 2268
  {
    // It is success (nobody can prevent us write data)
unknown's avatar
unknown committed
2269
    STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
2270
  }
unknown's avatar
unknown committed
2271

unknown's avatar
unknown committed
2272 2273
  // Now finally write data to the last block
  if (success && last_block_free_space > 0)
unknown's avatar
unknown committed
2274 2275
  {
    ulong to_copy = min(data_len,last_block_free_space);
unknown's avatar
unknown committed
2276
    DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub",
unknown's avatar
unknown committed
2277
			last_block_free_space, (ulong)last_block, to_copy));
2278
    memcpy((uchar*) last_block + last_block->used, data, to_copy);
unknown's avatar
unknown committed
2279 2280 2281 2282 2283
    last_block->used+=to_copy;
  }
  DBUG_RETURN(success);
}

unknown's avatar
unknown committed
2284 2285

my_bool Query_cache::write_result_data(Query_cache_block **result_block,
2286
				       ulong data_len, uchar* data,
unknown's avatar
unknown committed
2287
				       Query_cache_block *query_block,
unknown's avatar
unknown committed
2288 2289 2290
				       Query_cache_block::block_type type)
{
  DBUG_ENTER("Query_cache::write_result_data");
unknown's avatar
unknown committed
2291
  DBUG_PRINT("qcache", ("data_len %lu",data_len));
unknown's avatar
unknown committed
2292

unknown's avatar
unknown committed
2293 2294 2295 2296 2297 2298 2299 2300 2301
  /*
    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.
  */

2302 2303
  my_bool success = allocate_data_chain(result_block, data_len, query_block,
					type == Query_cache_block::RES_BEG);
unknown's avatar
unknown committed
2304 2305
  if (success)
  {
unknown's avatar
unknown committed
2306
    // It is success (nobody can prevent us write data)
unknown's avatar
unknown committed
2307
    STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
2308 2309
    uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
			ALIGN_SIZE(sizeof(Query_cache_result)));
unknown's avatar
SCRUM  
unknown committed
2310
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2311
    Query_cache_block *block= *result_block;
2312
    uchar *rest= data;
unknown's avatar
unknown committed
2313
    // Now fill list of blocks that created by allocate_data_chain
unknown's avatar
unknown committed
2314 2315 2316 2317
    do
    {
      block->type = type;
      ulong length = block->used - headers_len;
unknown's avatar
unknown committed
2318
      DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length,
unknown's avatar
SCRUM  
unknown committed
2319
			    (ulong)block));
2320
      memcpy((uchar*) block+headers_len, rest, length);
unknown's avatar
unknown committed
2321 2322 2323
      rest += length;
      block = block->next;
      type = Query_cache_block::RES_CONT;
unknown's avatar
unknown committed
2324
    } while (block != *result_block);
unknown's avatar
SCRUM  
unknown committed
2325
#else
2326 2327 2328 2329 2330
    /*
      Set type of first block, emb_store_querycache_result() will handle
      the others.
    */
    (*result_block)->type= type;
unknown's avatar
SCRUM  
unknown committed
2331 2332 2333
    Querycache_stream qs(*result_block, headers_len);
    emb_store_querycache_result(&qs, (THD*)data);
#endif /*!EMBEDDED_LIBRARY*/
unknown's avatar
unknown committed
2334 2335 2336
  }
  else
  {
unknown's avatar
unknown committed
2337
    if (*result_block != 0)
unknown's avatar
unknown committed
2338
    {
unknown's avatar
unknown committed
2339 2340
      // Destroy list of blocks that was created & locked by lock_result_data
      Query_cache_block *block = *result_block;
unknown's avatar
unknown committed
2341 2342
      do
      {
unknown's avatar
unknown committed
2343
	Query_cache_block *current = block;
unknown's avatar
unknown committed
2344 2345
	block = block->next;
	free_memory_block(current);
unknown's avatar
unknown committed
2346 2347
      } while (block != *result_block);
      *result_block = 0;
unknown's avatar
unknown committed
2348
      /*
unknown's avatar
unknown committed
2349 2350
	It is not success => not unlock structure_guard_mutex (we need it to
	free query)
unknown's avatar
unknown committed
2351 2352 2353
      */
    }
  }
unknown's avatar
unknown committed
2354
  DBUG_PRINT("qcache", ("success %d", (int) success));
unknown's avatar
unknown committed
2355 2356 2357
  DBUG_RETURN(success);
}

2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371
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;
}

unknown's avatar
unknown committed
2372 2373 2374 2375
/*
  Allocate one or more blocks to hold data
*/
my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
unknown's avatar
unknown committed
2376
					 ulong data_len,
2377
					 Query_cache_block *query_block,
2378
					 my_bool first_block_arg)
unknown's avatar
unknown committed
2379
{
unknown's avatar
unknown committed
2380 2381
  ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
			   ALIGN_SIZE(sizeof(Query_cache_result)));
2382
  ulong min_size = (first_block_arg ?
2383 2384
		    get_min_first_result_data_size():
		    get_min_append_result_data_size());
2385 2386 2387 2388 2389 2390 2391
  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
unknown's avatar
unknown committed
2392
  {
2393 2394 2395 2396 2397
    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,
2398
				    all_headers_len + min_result_data_size)))
2399 2400 2401 2402 2403
    {
      DBUG_PRINT("warning", ("Can't allocate block for results"));
      DBUG_RETURN(FALSE);
    }

unknown's avatar
unknown committed
2404
    new_block->n_tables = 0;
2405
    new_block->used = min(len, new_block->length);
unknown's avatar
unknown committed
2406 2407 2408
    new_block->type = Query_cache_block::RES_INCOMPLETE;
    new_block->next = new_block->prev = new_block;
    Query_cache_result *header = new_block->result();
unknown's avatar
unknown committed
2409 2410
    header->parent(query_block);

2411
    DBUG_PRINT("qcache", ("Block len %lu used %lu",
unknown's avatar
unknown committed
2412
			  new_block->length, new_block->used));
2413 2414 2415

    if (prev_block)
      double_linked_list_join(prev_block, new_block);
unknown's avatar
unknown committed
2416
    else
2417 2418 2419 2420 2421 2422 2423 2424 2425 2426
      *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;
unknown's avatar
unknown committed
2427
  } while (1);
2428 2429

  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2430 2431 2432
}

/*****************************************************************************
unknown's avatar
unknown committed
2433 2434 2435 2436 2437 2438
  Tables management
*****************************************************************************/

/*
  Invalidate the first table in the table_list
*/
unknown's avatar
unknown committed
2439

2440
void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
unknown's avatar
unknown committed
2441 2442
{
  if (table_list->table != 0)
2443
    invalidate_table(thd, table_list->table);	// Table is open
unknown's avatar
unknown committed
2444 2445
  else
  {
unknown's avatar
unknown committed
2446
    char key[MAX_DBKEY_LENGTH];
unknown's avatar
unknown committed
2447
    uint key_length;
2448

unknown's avatar
unknown committed
2449
    key_length=(uint) (strmov(strmov(key,table_list->db)+1,
2450
			      table_list->table_name) -key)+ 1;
unknown's avatar
unknown committed
2451

unknown's avatar
unknown committed
2452
    // We don't store temporary tables => no key_length+=4 ...
2453
    invalidate_table(thd, (uchar *)key, key_length);
unknown's avatar
unknown committed
2454 2455 2456
  }
}

2457
void Query_cache::invalidate_table(THD *thd, TABLE *table)
2458
{
2459
  invalidate_table(thd, (uchar*) table->s->table_cache_key.str,
unknown's avatar
unknown committed
2460
                   table->s->table_cache_key.length);
2461 2462
}

2463
void Query_cache::invalidate_table(THD *thd, uchar * key, uint32  key_length)
unknown's avatar
unknown committed
2464
{
2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482
  bool interrupt;
  STRUCT_LOCK(&structure_guard_mutex);
  wait_while_table_flush_is_in_progress(&interrupt);
  if (interrupt)
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    return;
  }

  /*
    Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache
    so that structural changes to cache won't block the entire server.
    However, threads requesting to change the query cache will still have
    to wait for the flush to finish.
  */
  m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS;
  STRUCT_UNLOCK(&structure_guard_mutex);

2483 2484
  if (query_cache_size > 0)
    invalidate_table_internal(thd, key, key_length);
2485 2486 2487 2488 2489 2490 2491 2492 2493 2494

  STRUCT_LOCK(&structure_guard_mutex);
  m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;

  /*
    net_real_write might be waiting on a change on the m_cache_status
    variable.
  */
  pthread_cond_signal(&COND_cache_status_changed);
  STRUCT_UNLOCK(&structure_guard_mutex);
unknown's avatar
unknown committed
2495 2496
}

2497

2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517
/**
  Try to locate and invalidate a table by name.
  The caller must ensure that no other thread is trying to work with
  the query cache when this function is executed.

  @pre structure_guard_mutex is acquired or TABLE_FLUSH_IN_PROGRESS is set.
*/

void
Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length)
{
  Query_cache_block *table_block=
    (Query_cache_block*)hash_search(&tables, key, key_length);
  if (table_block)
  {
    Query_cache_block_table *list_root= table_block->table(0);
    invalidate_query_block_list(thd, list_root);
  }
}

2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532
/**
  @brief Invalidate a linked list of query cache blocks.

  Each block tries to aquire a block level lock before
  free_query is a called. This function will in turn affect
  related table- and result-blocks.

  @param[in,out] thd Thread context.
  @param[in,out] list_root A pointer to a circular list of query blocks.

*/

void
Query_cache::invalidate_query_block_list(THD *thd,
                                         Query_cache_block_table *list_root)
unknown's avatar
unknown committed
2533
{
unknown's avatar
unknown committed
2534
  while (list_root->next != list_root)
unknown's avatar
unknown committed
2535
  {
2536
    Query_cache_block *query_block= list_root->next->block();
unknown's avatar
unknown committed
2537 2538
    BLOCK_LOCK_WR(query_block);
    free_query(query_block);
2539
    DBUG_EXECUTE_IF("debug_cache_locks", sleep(10););
unknown's avatar
unknown committed
2540 2541 2542
  }
}

unknown's avatar
unknown committed
2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557
/*
  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
*/

2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568
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++)
  {
2569
    if (tables_used->derived && !tables_used->view)
unknown's avatar
merge  
unknown committed
2570
    {
unknown's avatar
unknown committed
2571
      DBUG_PRINT("qcache", ("derived table skipped"));
unknown's avatar
merge  
unknown committed
2572 2573 2574 2575
      n--;
      block_table--;
      continue;
    }
2576 2577 2578 2579 2580
    block_table->n= n;
    if (tables_used->view)
    {
      char key[MAX_DBKEY_LENGTH];
      uint key_length;
unknown's avatar
unknown committed
2581
      DBUG_PRINT("qcache", ("view: %s  db: %s",
2582 2583 2584 2585
                            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;
unknown's avatar
merge  
unknown committed
2586 2587 2588 2589 2590 2591 2592
      /*
        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);
2593 2594 2595 2596
      /*
        We do not need to register view tables here because they are already
        present in the global list.
      */
2597 2598 2599 2600
    }
    else
    {
      DBUG_PRINT("qcache",
2601
                 ("table: %s  db: %s  openinfo:  0x%lx  keylen: %lu  key: 0x%lx",
unknown's avatar
unknown committed
2602 2603
                  tables_used->table->s->table_name.str,
                  tables_used->table->s->table_cache_key.str,
2604
                  (ulong) tables_used->table,
2605
                  (ulong) tables_used->table->s->table_cache_key.length,
unknown's avatar
unknown committed
2606
                  (ulong) tables_used->table->s->table_cache_key.str));
2607

unknown's avatar
unknown committed
2608 2609 2610
      if (!insert_table(tables_used->table->s->table_cache_key.length,
                        tables_used->table->s->table_cache_key.str,
                        block_table,
2611
                        tables_used->db_length,
unknown's avatar
merge  
unknown committed
2612 2613 2614
                        tables_used->table->file->table_cache_type(),
                        tables_used->callback_func,
                        tables_used->engine_data))
2615 2616
        DBUG_RETURN(0);

unknown's avatar
unknown committed
2617 2618 2619 2620 2621 2622
#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)
2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634
      {
        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;
unknown's avatar
merge  
unknown committed
2635 2636 2637
          /*
            There are not callback function for for MyISAM, and engine data
          */
2638 2639
          if (!insert_table(key_length, key, block_table,
                            db_length,
unknown's avatar
merge  
unknown committed
2640 2641
                            tables_used->table->file->table_cache_type(),
                            0, 0))
2642 2643 2644
            DBUG_RETURN(0);
        }
      }
unknown's avatar
unknown committed
2645
#endif
2646 2647 2648 2649 2650
    }
  }
  DBUG_RETURN(n - counter);
}

2651 2652 2653 2654 2655 2656 2657 2658 2659
/*
  Store all used tables

  SYNOPSIS
    register_all_tables()
    block		Store tables in this block
    tables_used		List if used tables
    tables_arg		Not used ?
*/
unknown's avatar
unknown committed
2660 2661 2662

my_bool Query_cache::register_all_tables(Query_cache_block *block,
					 TABLE_LIST *tables_used,
2663
					 TABLE_COUNTER_TYPE tables_arg)
unknown's avatar
unknown committed
2664
{
unknown's avatar
unknown committed
2665 2666
  TABLE_COUNTER_TYPE n;
  DBUG_PRINT("qcache", ("register tables block 0x%lx, n %d, header %x",
2667
		      (ulong) block, (int) tables_arg,
unknown's avatar
unknown committed
2668
		      (int) ALIGN_SIZE(sizeof(Query_cache_block))));
unknown's avatar
unknown committed
2669

unknown's avatar
unknown committed
2670 2671
  Query_cache_block_table *block_table = block->table(0);

2672
  n= register_tables_from_list(tables_used, 0, block_table);
unknown's avatar
unknown committed
2673

2674
  if (n==0)
unknown's avatar
unknown committed
2675 2676 2677 2678 2679 2680
  {
    /* Unlink the tables we allocated above */
    for (Query_cache_block_table *tmp = block->table(0) ;
	 tmp != block_table;
	 tmp++)
      unlink_table(tmp);
unknown's avatar
unknown committed
2681
  }
2682
  return (n);
unknown's avatar
unknown committed
2683 2684
}

2685 2686 2687 2688 2689 2690 2691

/**
  @brief Insert used table name into the cache.

  @return Error status
    @retval FALSE On error
    @retval TRUE On success
unknown's avatar
unknown committed
2692 2693 2694 2695 2696
*/

my_bool
Query_cache::insert_table(uint key_len, char *key,
			  Query_cache_block_table *node,
unknown's avatar
unknown committed
2697 2698 2699
			  uint32 db_length, uint8 cache_type,
                          qc_engine_callback callback,
                          ulonglong engine_data)
unknown's avatar
unknown committed
2700 2701
{
  DBUG_ENTER("Query_cache::insert_table");
unknown's avatar
unknown committed
2702
  DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
unknown's avatar
unknown committed
2703
		      (ulong)node, key_len));
unknown's avatar
unknown committed
2704

2705 2706 2707 2708
  THD *thd= current_thd;

  Query_cache_block *table_block= 
    (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len);
unknown's avatar
unknown committed
2709

unknown's avatar
unknown committed
2710 2711 2712 2713
  if (table_block &&
      table_block->table()->engine_data() != engine_data)
  {
    DBUG_PRINT("qcache",
unknown's avatar
unknown committed
2714
               ("Handler require invalidation queries of %s.%s %lu-%lu",
unknown's avatar
unknown committed
2715 2716
                table_block->table()->db(),
                table_block->table()->table(),
unknown's avatar
unknown committed
2717 2718
                (ulong) engine_data,
                (ulong) table_block->table()->engine_data()));
unknown's avatar
unknown committed
2719 2720 2721 2722
    /*
      as far as we delete all queries with this table, table block will be
      deleted, too
    */
2723 2724 2725 2726 2727
    {
      Query_cache_block_table *list_root= table_block->table(0);
      invalidate_query_block_list(thd, list_root);
    }

unknown's avatar
unknown committed
2728 2729 2730
    table_block= 0;
  }

unknown's avatar
unknown committed
2731 2732
  if (table_block == 0)
  {
unknown's avatar
unknown committed
2733 2734
    DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
			(ulong) key, (int) key_len));
2735 2736 2737
    table_block= write_block_data(key_len, (uchar*) key,
                                  ALIGN_SIZE(sizeof(Query_cache_table)),
                                  Query_cache_block::TABLE, 1);
unknown's avatar
unknown committed
2738 2739
    if (table_block == 0)
    {
unknown's avatar
unknown committed
2740
      DBUG_PRINT("qcache", ("Can't write table name to cache"));
unknown's avatar
unknown committed
2741 2742
      DBUG_RETURN(0);
    }
2743
    Query_cache_table *header= table_block->table();
unknown's avatar
unknown committed
2744
    double_linked_list_simple_include(table_block,
2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757
                                      &tables_blocks);
    /*
      First node in the Query_cache_block_table-chain is the table-type
      block. This block will only have one Query_cache_block_table (n=0).
    */
    Query_cache_block_table *list_root= table_block->table(0);
    list_root->n= 0;

    /*
      The node list is circular in nature.
    */
    list_root->next= list_root->prev= list_root;

2758
    if (my_hash_insert(&tables, (const uchar *) table_block))
unknown's avatar
unknown committed
2759
    {
unknown's avatar
unknown committed
2760
      DBUG_PRINT("qcache", ("Can't insert table to hash"));
unknown's avatar
unknown committed
2761 2762 2763 2764
      // write_block_data return locked block
      free_memory_block(table_block);
      DBUG_RETURN(0);
    }
2765
    char *db= header->db();
2766
    header->table(db + db_length + 1);
2767 2768
    header->key_length(key_len);
    header->type(cache_type);
unknown's avatar
unknown committed
2769 2770
    header->callback(callback);
    header->engine_data(engine_data);
2771 2772 2773 2774 2775 2776

    /*
      We insert this table without the assumption that it isn't refrenenced by
      any queries.
    */
    header->m_cached_query_count= 0;
unknown's avatar
unknown committed
2777 2778
  }

2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795
  /*
    Table is now in the cache; link the table_block-node associated
    with the currently processed query into the chain of queries depending
    on the cached table.
  */
  Query_cache_block_table *list_root= table_block->table(0);
  node->next= list_root->next;
  list_root->next= node;
  node->next->prev= node;
  node->prev= list_root;
  node->parent= table_block->table();
  /*
    Increase the counter to keep track on how long this chain
    of queries is.
  */
  Query_cache_table *table_block_data= table_block->table();
  table_block_data->m_cached_query_count++;
unknown's avatar
unknown committed
2796 2797 2798
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
2799 2800

void Query_cache::unlink_table(Query_cache_block_table *node)
unknown's avatar
unknown committed
2801
{
unknown's avatar
unknown committed
2802
  DBUG_ENTER("Query_cache::unlink_table");
2803 2804 2805 2806 2807 2808 2809 2810
  node->prev->next= node->next;
  node->next->prev= node->prev;
  Query_cache_block_table *neighbour= node->next;
  Query_cache_table *table_block_data= node->parent;
  table_block_data->m_cached_query_count--;

  DBUG_ASSERT(table_block_data->m_cached_query_count >= 0);

unknown's avatar
unknown committed
2811 2812
  if (neighbour->next == neighbour)
  {
2813 2814 2815 2816 2817 2818 2819 2820 2821
    DBUG_ASSERT(table_block_data->m_cached_query_count == 0);
    /*
      If neighbor is root of list, the list is empty.
      The root of the list is always a table-type block
      which contain exactly one Query_cache_block_table
      node object, thus we can use the block() method
      to calculate the Query_cache_block address.
    */
    Query_cache_block *table_block= neighbour->block();
unknown's avatar
unknown committed
2822
    double_linked_list_exclude(table_block,
2823
                               &tables_blocks);
2824
    hash_delete(&tables,(uchar *) table_block);
unknown's avatar
unknown committed
2825 2826
    free_memory_block(table_block);
  }
unknown's avatar
unknown committed
2827
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2828 2829 2830
}

/*****************************************************************************
unknown's avatar
unknown committed
2831 2832
  Free memory management
*****************************************************************************/
unknown's avatar
unknown committed
2833

unknown's avatar
unknown committed
2834
Query_cache_block *
2835
Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
unknown's avatar
unknown committed
2836
{
2837
  DBUG_ENTER("Query_cache::allocate_block");
2838 2839
  DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
             len, not_less,min));
unknown's avatar
unknown committed
2840 2841 2842

  if (len >= min(query_cache_size, query_cache_limit))
  {
unknown's avatar
unknown committed
2843
    DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu",
unknown's avatar
unknown committed
2844 2845 2846 2847
			query_cache_size, query_cache_limit));
    DBUG_RETURN(0); // in any case we don't have such piece of memory
  }

unknown's avatar
unknown committed
2848 2849 2850
  /* Free old queries until we have enough memory to store this block */
  Query_cache_block *block;
  do
unknown's avatar
unknown committed
2851
  {
unknown's avatar
unknown committed
2852
    block= get_free_block(len, not_less, min);
unknown's avatar
unknown committed
2853
  }
unknown's avatar
unknown committed
2854
  while (block == 0 && !free_old_query());
unknown's avatar
unknown committed
2855

unknown's avatar
unknown committed
2856
  if (block != 0)				// If we found a suitable block
unknown's avatar
unknown committed
2857
  {
2858
    if (block->length >= ALIGN_SIZE(len) + min_allocation_unit)
unknown's avatar
unknown committed
2859 2860 2861 2862 2863 2864
      split_block(block,ALIGN_SIZE(len));
  }

  DBUG_RETURN(block);
}

unknown's avatar
unknown committed
2865 2866 2867

Query_cache_block *
Query_cache::get_free_block(ulong len, my_bool not_less, ulong min)
unknown's avatar
unknown committed
2868 2869
{
  Query_cache_block *block = 0, *first = 0;
unknown's avatar
unknown committed
2870 2871
  DBUG_ENTER("Query_cache::get_free_block");
  DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len,
unknown's avatar
unknown committed
2872 2873
		     (int)not_less, min));

unknown's avatar
unknown committed
2874
  /* Find block with minimal size > len  */
unknown's avatar
unknown committed
2875 2876 2877 2878
  uint start = find_bin(len);
  // try matching bin
  if (bins[start].number != 0)
  {
unknown's avatar
unknown committed
2879
    Query_cache_block *list = bins[start].free_blocks;
unknown's avatar
unknown committed
2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901
    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++;
	}
2902
	if (block->length < len)
unknown's avatar
unknown committed
2903 2904 2905 2906 2907
	  block=block->next;
      }
    }
    else
      first = list->prev;
unknown's avatar
unknown committed
2908 2909 2910
  }
  if (block == 0 && start > 0)
  {
unknown's avatar
unknown committed
2911 2912
    DBUG_PRINT("qcache",("Try bins with bigger block size"));
    // Try more big bins
unknown's avatar
unknown committed
2913
    int i = start - 1;
unknown's avatar
unknown committed
2914
    while (i > 0 && bins[i].number == 0)
unknown's avatar
unknown committed
2915 2916 2917 2918
      i--;
    if (bins[i].number > 0)
      block = bins[i].free_blocks;
  }
unknown's avatar
unknown committed
2919 2920

  // If no big blocks => try less size (if it is possible)
unknown's avatar
unknown committed
2921 2922
  if (block == 0 && ! not_less)
  {
unknown's avatar
unknown committed
2923
    DBUG_PRINT("qcache",("Try to allocate a smaller block"));
unknown's avatar
unknown committed
2924 2925 2926 2927 2928
    if (first != 0 && first->length > min)
      block = first;
    else
    {
      uint i = start + 1;
unknown's avatar
unknown committed
2929 2930 2931
      /* 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)
unknown's avatar
unknown committed
2932 2933 2934 2935 2936 2937
	block = bins[i].free_blocks->prev;
    }
  }
  if (block != 0)
    exclude_from_free_memory_list(block);

unknown's avatar
unknown committed
2938
  DBUG_PRINT("qcache",("getting block 0x%lx", (ulong) block));
unknown's avatar
unknown committed
2939 2940 2941
  DBUG_RETURN(block);
}

unknown's avatar
unknown committed
2942 2943

void Query_cache::free_memory_block(Query_cache_block *block)
unknown's avatar
unknown committed
2944
{
unknown's avatar
unknown committed
2945
  DBUG_ENTER("Query_cache::free_memory_block");
unknown's avatar
unknown committed
2946
  block->used=0;
2947 2948 2949
  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",
2950
	      (ulong) first_block, (ulong) block, (ulong) block->pnext,
2951
	      (ulong) block->pprev));
unknown's avatar
unknown committed
2952

unknown's avatar
unknown committed
2953 2954 2955 2956 2957 2958 2959 2960
  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;
}

unknown's avatar
unknown committed
2961

2962
void Query_cache::split_block(Query_cache_block *block, ulong len)
unknown's avatar
unknown committed
2963 2964
{
  DBUG_ENTER("Query_cache::split_block");
2965
  Query_cache_block *new_block = (Query_cache_block*)(((uchar*) block)+len);
unknown's avatar
unknown committed
2966 2967

  new_block->init(block->length - len);
unknown's avatar
unknown committed
2968
  total_blocks++;
unknown's avatar
unknown committed
2969
  block->length=len;
unknown's avatar
unknown committed
2970 2971 2972 2973
  new_block->pnext = block->pnext;
  block->pnext = new_block;
  new_block->pprev = block;
  new_block->pnext->pprev = new_block;
unknown's avatar
unknown committed
2974

2975
  if (block->type == Query_cache_block::FREE)
2976
  {
2977 2978
    // if block was free then it already joined with all free neighbours
    insert_into_free_memory_list(new_block);
2979
  }
2980 2981
  else
    free_memory_block(new_block);
unknown's avatar
unknown committed
2982

unknown's avatar
unknown committed
2983
  DBUG_PRINT("qcache", ("split 0x%lx (%lu) new 0x%lx",
unknown's avatar
unknown committed
2984 2985 2986 2987
		      (ulong) block, len, (ulong) new_block));
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
2988

unknown's avatar
unknown committed
2989
Query_cache_block *
2990
Query_cache::join_free_blocks(Query_cache_block *first_block_arg,
unknown's avatar
unknown committed
2991
			      Query_cache_block *block_in_list)
unknown's avatar
unknown committed
2992
{
unknown's avatar
unknown committed
2993
  Query_cache_block *second_block;
unknown's avatar
unknown committed
2994
  DBUG_ENTER("Query_cache::join_free_blocks");
unknown's avatar
unknown committed
2995
  DBUG_PRINT("qcache",
unknown's avatar
unknown committed
2996
	     ("join first 0x%lx, pnext 0x%lx, in list 0x%lx",
2997
	      (ulong) first_block_arg, (ulong) first_block_arg->pnext,
unknown's avatar
unknown committed
2998 2999
	      (ulong) block_in_list));

unknown's avatar
unknown committed
3000
  exclude_from_free_memory_list(block_in_list);
3001
  second_block = first_block_arg->pnext;
unknown's avatar
unknown committed
3002 3003
  // May be was not free block
  second_block->used=0;
unknown's avatar
unknown committed
3004
  second_block->destroy();
unknown's avatar
unknown committed
3005
  total_blocks--;
unknown's avatar
unknown committed
3006

3007 3008 3009
  first_block_arg->length += second_block->length;
  first_block_arg->pnext = second_block->pnext;
  second_block->pnext->pprev = first_block_arg;
unknown's avatar
unknown committed
3010

3011
  DBUG_RETURN(first_block_arg);
unknown's avatar
unknown committed
3012 3013
}

unknown's avatar
unknown committed
3014 3015

my_bool Query_cache::append_next_free_block(Query_cache_block *block,
unknown's avatar
unknown committed
3016 3017
					    ulong add_size)
{
unknown's avatar
unknown committed
3018
  Query_cache_block *next_block = block->pnext;
unknown's avatar
unknown committed
3019 3020 3021
  DBUG_ENTER("Query_cache::append_next_free_block");
  DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block,
		       add_size));
unknown's avatar
unknown committed
3022

unknown's avatar
unknown committed
3023
  if (next_block != first_block && next_block->is_free())
unknown's avatar
unknown committed
3024 3025
  {
    ulong old_len = block->length;
unknown's avatar
unknown committed
3026
    exclude_from_free_memory_list(next_block);
unknown's avatar
unknown committed
3027
    next_block->destroy();
unknown's avatar
unknown committed
3028
    total_blocks--;
unknown's avatar
unknown committed
3029 3030 3031 3032 3033

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

unknown's avatar
unknown committed
3034
    if (block->length > ALIGN_SIZE(old_len + add_size) + min_allocation_unit)
unknown's avatar
unknown committed
3035 3036 3037 3038
      split_block(block,ALIGN_SIZE(old_len + add_size));
    DBUG_PRINT("exit", ("block was appended"));
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
3039
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3040 3041
}

unknown's avatar
unknown committed
3042 3043

void Query_cache::exclude_from_free_memory_list(Query_cache_block *free_block)
unknown's avatar
unknown committed
3044 3045
{
  DBUG_ENTER("Query_cache::exclude_from_free_memory_list");
unknown's avatar
unknown committed
3046 3047 3048
  Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
				  free_block->data());
  double_linked_list_exclude(free_block, &bin->free_blocks);
unknown's avatar
unknown committed
3049 3050
  bin->number--;
  free_memory-=free_block->length;
unknown's avatar
unknown committed
3051
  free_memory_blocks--;
unknown's avatar
unknown committed
3052 3053
  DBUG_PRINT("qcache",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block,
		     (ulong) bin));
unknown's avatar
unknown committed
3054 3055 3056
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
3057
void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block)
unknown's avatar
unknown committed
3058 3059 3060
{
  DBUG_ENTER("Query_cache::insert_into_free_memory_list");
  uint idx = find_bin(free_block->length);
unknown's avatar
unknown committed
3061
  insert_into_free_memory_sorted_list(free_block, &bins[idx].free_blocks);
unknown's avatar
unknown committed
3062
  /*
unknown's avatar
unknown committed
3063
    We have enough memory in block for storing bin reference due to
unknown's avatar
unknown committed
3064 3065
    min_allocation_unit choice
  */
unknown's avatar
unknown committed
3066 3067 3068 3069 3070 3071
  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));
unknown's avatar
unknown committed
3072 3073 3074 3075 3076 3077
  DBUG_VOID_RETURN;
}

uint Query_cache::find_bin(ulong size)
{
  DBUG_ENTER("Query_cache::find_bin");
unknown's avatar
unknown committed
3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088
  // 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)
unknown's avatar
unknown committed
3089 3090
  {
    // first bin not subordinate of common rules
unknown's avatar
unknown committed
3091
    DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size));
unknown's avatar
unknown committed
3092 3093
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
3094 3095
  uint bin =  steps[left].idx - 
    (uint)((size - steps[left].size)/steps[left].increment);
3096

unknown's avatar
unknown committed
3097 3098
  DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
			bin, left, size, steps[left].size));
unknown's avatar
unknown committed
3099 3100 3101
  DBUG_RETURN(bin);
}

unknown's avatar
unknown committed
3102

unknown's avatar
unknown committed
3103
/*****************************************************************************
unknown's avatar
unknown committed
3104 3105
 Lists management
*****************************************************************************/
unknown's avatar
unknown committed
3106

unknown's avatar
unknown committed
3107
void Query_cache::move_to_query_list_end(Query_cache_block *query_block)
unknown's avatar
unknown committed
3108 3109
{
  DBUG_ENTER("Query_cache::move_to_query_list_end");
unknown's avatar
unknown committed
3110 3111
  double_linked_list_exclude(query_block, &queries_blocks);
  double_linked_list_simple_include(query_block, &queries_blocks);
unknown's avatar
unknown committed
3112 3113 3114
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
3115

unknown's avatar
unknown committed
3116 3117
void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block *
						      new_block,
unknown's avatar
unknown committed
3118 3119
						      Query_cache_block **
						      list)
unknown's avatar
unknown committed
3120 3121 3122 3123
{
  DBUG_ENTER("Query_cache::insert_into_free_memory_sorted_list");
  /*
     list sorted by size in ascendant order, because we need small blocks
unknown's avatar
unknown committed
3124
     more frequently than bigger ones
unknown's avatar
unknown committed
3125 3126 3127 3128 3129 3130
  */

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

unknown's avatar
unknown committed
3131
  if (*list == 0)
unknown's avatar
unknown committed
3132
  {
unknown's avatar
unknown committed
3133 3134
    *list = new_block->next=new_block->prev=new_block;
    DBUG_PRINT("qcache", ("inserted into empty list"));
unknown's avatar
unknown committed
3135 3136 3137
  }
  else
  {
unknown's avatar
unknown committed
3138
    Query_cache_block *point = *list;
unknown's avatar
unknown committed
3139 3140 3141
    if (point->length >= new_block->length)
    {
      point = point->prev;
unknown's avatar
unknown committed
3142
      *list = new_block;
unknown's avatar
unknown committed
3143 3144 3145
    }
    else
    {
unknown's avatar
unknown committed
3146 3147 3148
      /* Find right position in sorted list to put block */
      while (point->next != *list &&
	     point->next->length < new_block->length)
unknown's avatar
unknown committed
3149 3150
	point=point->next;
    }
unknown's avatar
unknown committed
3151 3152 3153 3154
    new_block->prev = point;
    new_block->next = point->next;
    new_block->next->prev = new_block;
    point->next = new_block;
unknown's avatar
unknown committed
3155 3156
  }
  free_memory+=new_block->length;
unknown's avatar
unknown committed
3157
  free_memory_blocks++;
unknown's avatar
unknown committed
3158 3159 3160 3161 3162
  DBUG_VOID_RETURN;
}


void
unknown's avatar
unknown committed
3163 3164 3165
Query_cache::double_linked_list_simple_include(Query_cache_block *point,
						Query_cache_block **
						list_pointer)
unknown's avatar
unknown committed
3166 3167
{
  DBUG_ENTER("Query_cache::double_linked_list_simple_include");
unknown's avatar
unknown committed
3168 3169 3170
  DBUG_PRINT("qcache", ("including block 0x%lx", (ulong) point));
  if (*list_pointer == 0)
    *list_pointer=point->next=point->prev=point;
unknown's avatar
unknown committed
3171 3172
  else
  {
unknown's avatar
unknown committed
3173
    // insert to the end of list
unknown's avatar
unknown committed
3174 3175
    point->next = (*list_pointer);
    point->prev = (*list_pointer)->prev;
unknown's avatar
unknown committed
3176
    point->prev->next = point;
unknown's avatar
unknown committed
3177
    (*list_pointer)->prev = point;
unknown's avatar
unknown committed
3178 3179 3180 3181 3182 3183
  }
  DBUG_VOID_RETURN;
}

void
Query_cache::double_linked_list_exclude(Query_cache_block *point,
unknown's avatar
unknown committed
3184
					Query_cache_block **list_pointer)
unknown's avatar
unknown committed
3185 3186
{
  DBUG_ENTER("Query_cache::double_linked_list_exclude");
unknown's avatar
unknown committed
3187
  DBUG_PRINT("qcache", ("excluding block 0x%lx, list 0x%lx",
unknown's avatar
unknown committed
3188 3189
		      (ulong) point, (ulong) list_pointer));
  if (point->next == point)
unknown's avatar
unknown committed
3190
    *list_pointer = 0;				// empty list
unknown's avatar
unknown committed
3191 3192 3193 3194
  else
  {
    point->next->prev = point->prev;
    point->prev->next = point->next;
3195 3196 3197
    /*
       If the root is removed; select a new root
    */
unknown's avatar
unknown committed
3198
    if (point == *list_pointer)
3199
      *list_pointer= point->next;
unknown's avatar
unknown committed
3200 3201 3202 3203
  }
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
3204

unknown's avatar
unknown committed
3205 3206 3207 3208
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,
unknown's avatar
unknown committed
3209
		    *tail_tail	= tail_head->prev;
unknown's avatar
unknown committed
3210 3211 3212 3213 3214 3215 3216
  head_head->prev = tail_tail;
  head_tail->next = tail_head;
  tail_head->prev = head_tail;
  tail_tail->next = head_head;
}

/*****************************************************************************
unknown's avatar
unknown committed
3217 3218
 Query
*****************************************************************************/
unknown's avatar
unknown committed
3219

3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240
/*
  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)
  {
unknown's avatar
unknown committed
3241
    table_count++;
3242 3243
    if (tables_used->view)
    {
unknown's avatar
unknown committed
3244
      DBUG_PRINT("qcache", ("view: %s  db: %s",
3245 3246 3247 3248 3249 3250
                            tables_used->view_name.str,
                            tables_used->view_db.str));
      *tables_type|= HA_CACHE_TBL_NONTRANSACT;
    }
    else
    {
unknown's avatar
unknown committed
3251 3252 3253
      DBUG_PRINT("qcache", ("table: %s  db:  %s  type: %u",
                            tables_used->table->s->table_name.str,
                            tables_used->table->s->db.str,
unknown's avatar
unknown committed
3254
                            tables_used->table->s->db_type()->db_type));
unknown's avatar
unknown committed
3255
      if (tables_used->derived)
unknown's avatar
merge  
unknown committed
3256 3257 3258 3259 3260
      {
        table_count--;
        DBUG_PRINT("qcache", ("derived table skipped"));
        continue;
      }
3261 3262 3263 3264 3265 3266 3267 3268 3269 3270
      *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,
unknown's avatar
unknown committed
3271
                        (uchar*)tables_used->table->s->table_cache_key.str, 6,
3272 3273 3274
                        (uchar*)"mysql",6) == 0))
      {
        DBUG_PRINT("qcache",
unknown's avatar
unknown committed
3275 3276
                   ("select not cacheable: temporary, system or "
                    "other non-cacheable table(s)"));
3277 3278
        DBUG_RETURN(0);
      }
unknown's avatar
unknown committed
3279 3280 3281 3282 3283 3284
#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)
3285 3286 3287 3288 3289
      {
        ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file;
        MYRG_INFO *file = handler->myrg_info();
        table_count+= (file->end_table - file->open_tables);
      }
unknown's avatar
unknown committed
3290
#endif
3291 3292 3293 3294 3295 3296
    }
  }
  DBUG_RETURN(table_count);
}


unknown's avatar
unknown committed
3297
/*
3298
  If query is cacheable return number tables in query
unknown's avatar
unknown committed
3299
  (query without tables are not cached)
unknown's avatar
unknown committed
3300
*/
unknown's avatar
unknown committed
3301

3302 3303 3304
TABLE_COUNTER_TYPE
Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
                          TABLE_LIST *tables_used, uint8 *tables_type)
unknown's avatar
unknown committed
3305
{
3306
  TABLE_COUNTER_TYPE table_count;
unknown's avatar
unknown committed
3307 3308
  DBUG_ENTER("Query_cache::is_cacheable");

3309
  if (query_cache_is_cacheable_query(lex) &&
unknown's avatar
unknown committed
3310
      (thd->variables.query_cache_type == 1 ||
3311
       (thd->variables.query_cache_type == 2 && (lex->select_lex.options &
3312
						 OPTION_TO_QUERY_CACHE))))
unknown's avatar
unknown committed
3313
  {
unknown's avatar
unknown committed
3314
    DBUG_PRINT("qcache", ("options: %lx  %lx  type: %u",
unknown's avatar
unknown committed
3315
                          (long) OPTION_TO_QUERY_CACHE,
unknown's avatar
unknown committed
3316 3317
                          (long) lex->select_lex.options,
                          (int) thd->variables.query_cache_type));
unknown's avatar
unknown committed
3318

3319 3320
    if (!(table_count= process_and_count_tables(tables_used, tables_type)))
      DBUG_RETURN(0);
unknown's avatar
unknown committed
3321

unknown's avatar
unknown committed
3322
    if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
3323
	((*tables_type)&HA_CACHE_TBL_TRANSACT))
unknown's avatar
unknown committed
3324
    {
unknown's avatar
unknown committed
3325
      DBUG_PRINT("qcache", ("not in autocommin mode"));
unknown's avatar
unknown committed
3326 3327
      DBUG_RETURN(0);
    }
3328 3329
    DBUG_PRINT("qcache", ("select is using %d tables", table_count));
    DBUG_RETURN(table_count);
unknown's avatar
unknown committed
3330
  }
unknown's avatar
unknown committed
3331 3332

  DBUG_PRINT("qcache",
unknown's avatar
unknown committed
3333
	     ("not interesting query: %d or not cacheable, options %lx %lx  type: %u",
unknown's avatar
unknown committed
3334
	      (int) lex->sql_command,
unknown's avatar
unknown committed
3335
	      (long) OPTION_TO_QUERY_CACHE,
unknown's avatar
unknown committed
3336
	      (long) lex->select_lex.options,
unknown's avatar
unknown committed
3337
	      (int) thd->variables.query_cache_type));
unknown's avatar
unknown committed
3338 3339 3340
  DBUG_RETURN(0);
}

3341
/*
unknown's avatar
unknown committed
3342
  Check handler allowance to cache query with these tables
3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355

  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)
{
unknown's avatar
unknown committed
3356
  DBUG_ENTER("Query_cache::ask_handler_allowance");
3357

unknown's avatar
VIEW  
unknown committed
3358
  for (; tables_used; tables_used= tables_used->next_global)
3359
  {
unknown's avatar
merge  
unknown committed
3360
    TABLE *table;
unknown's avatar
unknown committed
3361
    handler *handler;
unknown's avatar
merge  
unknown committed
3362 3363
    if (!(table= tables_used->table))
      continue;
unknown's avatar
unknown committed
3364 3365 3366 3367
    handler= table->file;
    if (!handler->register_query_cache_table(thd,
                                             table->s->table_cache_key.str,
					     table->s->table_cache_key.length,
3368 3369
					     &tables_used->callback_func,
					     &tables_used->engine_data))
3370 3371 3372
    {
      DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
			    tables_used->db, tables_used->alias));
unknown's avatar
unknown committed
3373
      thd->lex->safe_to_cache_query= 0;          // Don't try to cache this
3374 3375 3376 3377 3378 3379
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
3380

unknown's avatar
unknown committed
3381
/*****************************************************************************
unknown's avatar
unknown committed
3382 3383
  Packing
*****************************************************************************/
unknown's avatar
unknown committed
3384

3385 3386 3387 3388 3389 3390 3391

/**
  @brief Rearrange all memory blocks so that free memory joins at the
    'bottom' of the allocated memory block containing all cache data.
  @see Query_cache::pack(ulong join_limit, uint iteration_limit)
*/

unknown's avatar
unknown committed
3392 3393
void Query_cache::pack_cache()
{
unknown's avatar
unknown committed
3394
  DBUG_ENTER("Query_cache::pack_cache");
3395

unknown's avatar
unknown committed
3396 3397
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););

3398
  uchar *border = 0;
unknown's avatar
unknown committed
3399
  Query_cache_block *before = 0;
unknown's avatar
unknown committed
3400 3401
  ulong gap = 0;
  my_bool ok = 1;
unknown's avatar
unknown committed
3402
  Query_cache_block *block = first_block;
unknown's avatar
unknown committed
3403 3404
  DUMP(this);

unknown's avatar
unknown committed
3405
  if (first_block)
unknown's avatar
unknown committed
3406
  {
unknown's avatar
unknown committed
3407 3408
    do
    {
unknown's avatar
unknown committed
3409
      Query_cache_block *next=block->pnext;
unknown's avatar
unknown committed
3410
      ok = move_by_type(&border, &before, &gap, block);
unknown's avatar
unknown committed
3411
      block = next;
unknown's avatar
unknown committed
3412
    } while (ok && block != first_block);
unknown's avatar
unknown committed
3413

unknown's avatar
unknown committed
3414 3415 3416 3417
    if (border != 0)
    {
      Query_cache_block *new_block = (Query_cache_block *) border;
      new_block->init(gap);
unknown's avatar
unknown committed
3418
      total_blocks++;
unknown's avatar
unknown committed
3419 3420 3421 3422 3423 3424 3425
      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);
unknown's avatar
unknown committed
3426
  }
unknown's avatar
unknown committed
3427 3428

  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
unknown's avatar
unknown committed
3429 3430 3431
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
3432

3433
my_bool Query_cache::move_by_type(uchar **border,
unknown's avatar
unknown committed
3434 3435
				  Query_cache_block **before, ulong *gap,
				  Query_cache_block *block)
unknown's avatar
unknown committed
3436 3437
{
  DBUG_ENTER("Query_cache::move_by_type");
unknown's avatar
unknown committed
3438

unknown's avatar
unknown committed
3439
  my_bool ok = 1;
unknown's avatar
unknown committed
3440
  switch (block->type) {
unknown's avatar
unknown committed
3441
  case Query_cache_block::FREE:
unknown's avatar
unknown committed
3442 3443 3444
  {
    DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block));
    if (*border == 0)
unknown's avatar
unknown committed
3445
    {
3446
      *border = (uchar *) block;
unknown's avatar
unknown committed
3447 3448
      *before = block->pprev;
      DBUG_PRINT("qcache", ("gap beginning here"));
unknown's avatar
unknown committed
3449
    }
unknown's avatar
unknown committed
3450 3451 3452 3453 3454
    exclude_from_free_memory_list(block);
    *gap +=block->length;
    block->pprev->pnext=block->pnext;
    block->pnext->pprev=block->pprev;
    block->destroy();
unknown's avatar
unknown committed
3455
    total_blocks--;
unknown's avatar
unknown committed
3456 3457 3458
    DBUG_PRINT("qcache", ("added to gap (%lu)", *gap));
    break;
  }
unknown's avatar
unknown committed
3459
  case Query_cache_block::TABLE:
unknown's avatar
unknown committed
3460
  {
3461
    HASH_SEARCH_STATE record_idx;
unknown's avatar
unknown committed
3462 3463
    DBUG_PRINT("qcache", ("block 0x%lx TABLE", (ulong) block));
    if (*border == 0)
unknown's avatar
unknown committed
3464
      break;
unknown's avatar
unknown committed
3465 3466 3467 3468 3469 3470 3471 3472 3473
    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;
3474
    uint tablename_offset = block->table()->table() - block->table()->db();
unknown's avatar
unknown committed
3475
    char *data = (char*) block->data();
3476 3477 3478 3479
    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);
unknown's avatar
unknown committed
3480 3481 3482 3483 3484 3485

    block->destroy();
    new_block->init(len);
    new_block->type=Query_cache_block::TABLE;
    new_block->used=used;
    new_block->n_tables=1;
3486
    memmove((char*) new_block->data(), data, len-new_block->headers_len());
unknown's avatar
unknown committed
3487
    relink(block, new_block, next, prev, pnext, pprev);
3488 3489
    if (tables_blocks == block)
      tables_blocks = new_block;
unknown's avatar
unknown committed
3490 3491 3492

    Query_cache_block_table *nlist_root = new_block->table(0);
    nlist_root->n = 0;
unknown's avatar
unknown committed
3493 3494 3495 3496 3497 3498 3499 3500
    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));
3501 3502 3503 3504 3505
    /*
      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();
unknown's avatar
unknown committed
3506
    for (;tnext != nlist_root; tnext=tnext->next)
3507
      tnext->parent= new_block_table;
unknown's avatar
unknown committed
3508 3509
    *border += len;
    *before = new_block;
3510 3511
    /* Fix pointer to table name */
    new_block->table()->table(new_block->table()->db() + tablename_offset);
unknown's avatar
unknown committed
3512
    /* Fix hash to point at moved block */
3513
    hash_replace(&tables, &record_idx, (uchar*) new_block);
unknown's avatar
unknown committed
3514 3515 3516 3517 3518

    DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
			len, (ulong) new_block, (ulong) *border));
    break;
  }
unknown's avatar
unknown committed
3519
  case Query_cache_block::QUERY:
unknown's avatar
unknown committed
3520
  {
3521
    HASH_SEARCH_STATE record_idx;
unknown's avatar
unknown committed
3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535
    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();
3536 3537 3538 3539
    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);
3540 3541
    // Move table of used tables 
    memmove((char*) new_block->table(0), (char*) block->table(0),
unknown's avatar
unknown committed
3542 3543 3544 3545 3546 3547 3548
	   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;
3549
    memmove((char*) new_block->data(), data, len - new_block->headers_len());
unknown's avatar
unknown committed
3550 3551 3552
    relink(block, new_block, next, prev, pnext, pprev);
    if (queries_blocks == block)
      queries_blocks = new_block;
3553 3554
    Query_cache_block_table *beg_of_table_table= block->table(0),
      *end_of_table_table= block->table(n_tables);
3555
    uchar *beg_of_new_table_table= (uchar*) new_block->table(0);
3556
      
unknown's avatar
unknown committed
3557
    for (TABLE_COUNTER_TYPE j=0; j < n_tables; j++)
unknown's avatar
unknown committed
3558
    {
unknown's avatar
unknown committed
3559
      Query_cache_block_table *block_table = new_block->table(j);
3560 3561 3562 3563 3564

      // 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 + 
3565 3566
				     (((uchar*)block_table->next) -
				      ((uchar*)beg_of_table_table))))->prev=
3567 3568 3569 3570 3571 3572 3573 3574
	 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 + 
3575 3576
				     (((uchar*)block_table->prev) -
				      ((uchar*)beg_of_table_table))))->next=
3577 3578 3579
	  block_table;
      else
	block_table->prev->next = block_table;
unknown's avatar
unknown committed
3580 3581 3582 3583 3584 3585 3586 3587 3588
    }
    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
unknown's avatar
unknown committed
3589
      {
unknown's avatar
unknown committed
3590 3591 3592 3593
	result_block->result()->parent(new_block);
	result_block = result_block->next;
      } while ( result_block != first_result_block );
    }
unknown's avatar
unknown committed
3594
    Query_cache_query *new_query= ((Query_cache_query *) new_block->data());
unknown's avatar
unknown committed
3595
    my_rwlock_init(&new_query->lock, NULL);
3596

3597 3598 3599 3600
    /* 
      If someone is writing to this block, inform the writer that the block
      has been moved.
    */
unknown's avatar
unknown committed
3601 3602 3603
    NET *net = new_block->query()->writer();
    if (net != 0)
    {
3604
      net->query_cache_query= (uchar*) new_block;
unknown's avatar
unknown committed
3605
    }
unknown's avatar
unknown committed
3606
    /* Fix hash to point at moved block */
3607
    hash_replace(&queries, &record_idx, (uchar*) new_block);
unknown's avatar
unknown committed
3608 3609 3610 3611
    DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx",
			len, (ulong) new_block, (ulong) *border));
    break;
  }
unknown's avatar
unknown committed
3612 3613 3614 3615
  case Query_cache_block::RES_INCOMPLETE:
  case Query_cache_block::RES_BEG:
  case Query_cache_block::RES_CONT:
  case Query_cache_block::RESULT:
unknown's avatar
unknown committed
3616 3617 3618 3619
  {
    DBUG_PRINT("qcache", ("block 0x%lx RES* (%d)", (ulong) block,
			(int) block->type));
    if (*border == 0)
unknown's avatar
unknown committed
3620
      break;
unknown's avatar
unknown committed
3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634
    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;
3635
    memmove((char*) new_block->data(), data, len - new_block->headers_len());
unknown's avatar
unknown committed
3636 3637 3638 3639 3640 3641 3642 3643
    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 */
unknown's avatar
unknown committed
3644 3645
    ulong free_space= new_block->length - new_block->used;
    free_space-= free_space % ALIGN_SIZE(1);
unknown's avatar
unknown committed
3646 3647 3648 3649 3650
    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)
    {
unknown's avatar
unknown committed
3651 3652 3653 3654
      *border-= free_space;
      *gap+= free_space;
      DBUG_PRINT("qcache",
		 ("rest of result free space added to gap (%lu)", *gap));
unknown's avatar
unknown committed
3655
      new_block->length -= free_space;
unknown's avatar
unknown committed
3656
    }
unknown's avatar
unknown committed
3657 3658 3659 3660 3661
    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;
  }
unknown's avatar
unknown committed
3662
  default:
unknown's avatar
unknown committed
3663 3664
    DBUG_PRINT("error", ("unexpected block type %d, block 0x%lx",
			 (int)block->type, (ulong) block));
unknown's avatar
unknown committed
3665 3666 3667 3668 3669
    ok = 0;
  }
  DBUG_RETURN(ok);
}

unknown's avatar
unknown committed
3670 3671 3672 3673 3674

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)
unknown's avatar
unknown committed
3675
{
unknown's avatar
unknown committed
3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690
  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;
  }
unknown's avatar
unknown committed
3691
  nblock->pprev = pprev; // Physical pointer to himself have only 1 free block
unknown's avatar
unknown committed
3692 3693 3694 3695 3696
  nblock->pnext = pnext;
  pprev->pnext=nblock;
  pnext->pprev=nblock;
}

unknown's avatar
unknown committed
3697

unknown's avatar
unknown committed
3698 3699 3700
my_bool Query_cache::join_results(ulong join_limit)
{
  my_bool has_moving = 0;
unknown's avatar
unknown committed
3701 3702
  DBUG_ENTER("Query_cache::join_results");

3703
  if (queries_blocks != 0)
unknown's avatar
unknown committed
3704
  {
3705
    DBUG_ASSERT(query_cache_size > 0);
unknown's avatar
unknown committed
3706
    Query_cache_block *block = queries_blocks;
unknown's avatar
unknown committed
3707 3708
    do
    {
unknown's avatar
unknown committed
3709
      Query_cache_query *header = block->query();
unknown's avatar
unknown committed
3710 3711 3712 3713 3714
      if (header->result() != 0 &&
	  header->result()->type == Query_cache_block::RESULT &&
	  header->length() > join_limit)
      {
	Query_cache_block *new_result_block =
unknown's avatar
unknown committed
3715
	  get_free_block(ALIGN_SIZE(header->length()) +
unknown's avatar
unknown committed
3716 3717 3718 3719 3720 3721
			 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();
unknown's avatar
unknown committed
3722 3723 3724
	  ulong new_len = (header->length() +
			   ALIGN_SIZE(sizeof(Query_cache_block)) +
			   ALIGN_SIZE(sizeof(Query_cache_result)));
unknown's avatar
unknown committed
3725 3726
	  if (new_result_block->length >
	      ALIGN_SIZE(new_len) + min_allocation_unit)
unknown's avatar
unknown committed
3727 3728
	    split_block(new_result_block, ALIGN_SIZE(new_len));
	  BLOCK_LOCK_WR(block);
unknown's avatar
unknown committed
3729 3730 3731 3732 3733 3734
	  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;
unknown's avatar
unknown committed
3735
	  DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)",
unknown's avatar
unknown committed
3736 3737 3738 3739 3740
			      new_result_block->length,
			      new_result_block->used,
			      header->length()));

	  Query_cache_result *new_result = new_result_block->result();
unknown's avatar
unknown committed
3741
	  new_result->parent(block);
3742
	  uchar *write_to = (uchar*) new_result->data();
unknown's avatar
unknown committed
3743 3744 3745
	  Query_cache_block *result_block = first_result;
	  do
	  {
unknown's avatar
unknown committed
3746 3747
	    ulong len = (result_block->used - result_block->headers_len() -
			 ALIGN_SIZE(sizeof(Query_cache_result)));
unknown's avatar
unknown committed
3748 3749 3750 3751 3752 3753 3754 3755
	    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;
unknown's avatar
unknown committed
3756
	    Query_cache_block *old_result_block = result_block;
unknown's avatar
unknown committed
3757 3758 3759
	    result_block = result_block->next;
	    free_memory_block(old_result_block);
	  } while (result_block != first_result);
unknown's avatar
unknown committed
3760
	  BLOCK_UNLOCK_WR(block);
unknown's avatar
unknown committed
3761 3762
	}
      }
unknown's avatar
unknown committed
3763 3764
      block = block->next;
    } while ( block != queries_blocks );
unknown's avatar
unknown committed
3765 3766 3767 3768
  }
  DBUG_RETURN(has_moving);
}

unknown's avatar
unknown committed
3769

3770 3771
uint Query_cache::filename_2_table_key (char *key, const char *path,
					uint32 *db_length)
unknown's avatar
unknown committed
3772
{
unknown's avatar
unknown committed
3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783
  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--) ;
3784 3785
  *db_length= (filename - dbname) - 1;
  DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename));
unknown's avatar
unknown committed
3786

3787
  DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1,
unknown's avatar
unknown committed
3788 3789 3790 3791 3792 3793 3794
			     filename) -key) + 1);
}

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

3795 3796
#if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK)

3797
void wreck(uint line, const char *message) { query_cache_size = 0; }
3798 3799 3800 3801
void bins_dump() {}
void cache_dump() {}
void queries_dump() {}
void tables_dump() {}
3802
my_bool check_integrity(bool not_locked) { return 0; }
3803 3804 3805 3806 3807
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
unknown's avatar
unknown committed
3808

3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819

/*
  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
*/

unknown's avatar
unknown committed
3820 3821
void Query_cache::wreck(uint line, const char *message)
{
unknown's avatar
unknown committed
3822
  THD *thd=current_thd;
unknown's avatar
unknown committed
3823 3824 3825 3826 3827 3828 3829
  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", ("=================================="));
unknown's avatar
unknown committed
3830
  if (thd)
unknown's avatar
SCRUM  
unknown committed
3831
    thd->killed= THD::KILL_CONNECTION;
unknown's avatar
unknown committed
3832
  cache_dump();
3833
  /* check_integrity(0); */ /* Can't call it here because of locks */
unknown's avatar
unknown committed
3834
  bins_dump();
unknown's avatar
unknown committed
3835 3836 3837 3838 3839 3840 3841
  DBUG_VOID_RETURN;
}


void Query_cache::bins_dump()
{
  uint i;
unknown's avatar
unknown committed
3842
  
3843
  if (!initialized || query_cache_size == 0)
unknown's avatar
unknown committed
3844 3845 3846 3847 3848
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

unknown's avatar
unknown committed
3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883
  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()
{
3884
  if (!initialized || query_cache_size == 0)
unknown's avatar
unknown committed
3885 3886 3887 3888 3889
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

unknown's avatar
unknown committed
3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906
  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", ("-------------------------------------"));
}

unknown's avatar
unknown committed
3907

unknown's avatar
unknown committed
3908 3909
void Query_cache::queries_dump()
{
unknown's avatar
unknown committed
3910

3911
  if (!initialized)
unknown's avatar
unknown committed
3912 3913 3914 3915 3916
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

unknown's avatar
unknown committed
3917 3918 3919 3920 3921 3922 3923 3924
  DBUG_PRINT("qcache", ("------------------"));
  DBUG_PRINT("qcache", (" QUERIES"));
  DBUG_PRINT("qcache", ("------------------"));
  if (queries_blocks != 0)
  {
    Query_cache_block *block = queries_blocks;
    do
    {
3925 3926
      size_t len;
      char *str = (char*) query_cache_query_get_key((uchar*) block, &len, 0);
3927 3928 3929 3930
      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
3931
      DBUG_PRINT("qcache", ("F: %u  C: %u L: %lu  T: '%s' (%lu)  '%s'  '%s'",
3932
			    flags.client_long_flag,
3933
			    flags.character_set_client_num, 
unknown's avatar
unknown committed
3934 3935
                            (ulong)flags.limit,
                            flags.time_zone->get_name()->ptr(),
3936
			    (ulong) len, str, strend(str)+1));
unknown's avatar
unknown committed
3937 3938 3939
      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));
3940 3941
      memcpy(str + len, &flags, QUERY_CACHE_FLAGS_SIZE); // restore flags
      for (TABLE_COUNTER_TYPE t= 0; t < block->n_tables; t++)
unknown's avatar
unknown committed
3942
      {
3943
	Query_cache_table *table= block->table(t)->parent;
unknown's avatar
unknown committed
3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971
	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", ("------------------"));
}
unknown's avatar
unknown committed
3972 3973


unknown's avatar
unknown committed
3974 3975
void Query_cache::tables_dump()
{
3976
  if (!initialized || query_cache_size == 0)
unknown's avatar
unknown committed
3977 3978 3979 3980 3981
  {
    DBUG_PRINT("qcache", ("Query Cache not initialized"));
    return;
  }

unknown's avatar
unknown committed
3982 3983 3984
  DBUG_PRINT("qcache", ("--------------------"));
  DBUG_PRINT("qcache", ("TABLES"));
  DBUG_PRINT("qcache", ("--------------------"));
3985
  if (tables_blocks != 0)
unknown's avatar
unknown committed
3986
  {
3987 3988
    Query_cache_block *table_block = tables_blocks;
    do
unknown's avatar
unknown committed
3989
    {
3990 3991 3992
      Query_cache_table *table = table_block->table();
      DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table()));
      table_block = table_block->next;
3993
    } while (table_block != tables_blocks);
unknown's avatar
unknown committed
3994
  }
3995 3996
  else
    DBUG_PRINT("qcache", ("no tables in list"));
unknown's avatar
unknown committed
3997
  DBUG_PRINT("qcache", ("--------------------"));
unknown's avatar
unknown committed
3998
}
unknown's avatar
unknown committed
3999 4000


4001 4002 4003 4004 4005 4006 4007 4008
/**
  @brief Checks integrity of the various linked lists

  @return Error status code
    @retval FALSE Query cache is operational.
    @retval TRUE Query cache is broken.
*/

4009
my_bool Query_cache::check_integrity(bool locked)
unknown's avatar
unknown committed
4010 4011 4012
{
  my_bool result = 0;
  uint i;
4013
  DBUG_ENTER("check_integrity");
unknown's avatar
unknown committed
4014

4015 4016 4017
  if (!locked)
    STRUCT_LOCK(&structure_guard_mutex);

4018 4019
  while (is_flushing())
    pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex);
unknown's avatar
unknown committed
4020

unknown's avatar
unknown committed
4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037
  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
  {
unknown's avatar
unknown committed
4038 4039 4040 4041
    /* When checking at system start, there is no block. */
    if (!block)
      break;

unknown's avatar
unknown committed
4042 4043
    DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			  (ulong) block, (uint) block->type));  
4044
    // Check allignment
4045 4046
    if ((((long)block) % (long) ALIGN_SIZE(1)) !=
	(((long)first_block) % (long)ALIGN_SIZE(1)))
4047 4048 4049
    {
      DBUG_PRINT("error",
		 ("block 0x%lx do not aligned by %d", (ulong) block,
unknown's avatar
unknown committed
4050
		  (int) ALIGN_SIZE(1)));
4051 4052
      result = 1;
    }
unknown's avatar
unknown committed
4053 4054 4055
    // Check memory allocation
    if (block->pnext == first_block) // Is it last block?
    {
4056 4057
      if (((uchar*)block) + block->length != 
	  ((uchar*)first_block) + query_cache_size)
unknown's avatar
unknown committed
4058 4059 4060 4061
      {
	DBUG_PRINT("error", 
		   ("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx",
		    (ulong) block, (uint) block->type, 
4062 4063
		    (ulong) (((uchar*)block) + block->length),
		    (ulong) (((uchar*)first_block) + query_cache_size)));
unknown's avatar
unknown committed
4064 4065 4066 4067
	result = 1;
      }
    }
    else
4068
      if (((uchar*)block) + block->length != ((uchar*)block->pnext))
unknown's avatar
unknown committed
4069 4070 4071 4072
      {
	DBUG_PRINT("error", 
		   ("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx",
		    (ulong) block, (uint) block->type, 
4073 4074
		    (ulong) (((uchar*)block) + block->length),
		    (ulong) ((uchar*)block->pnext)));
unknown's avatar
unknown committed
4075 4076
      }
    if (block->type == Query_cache_block::FREE)
4077
      free+= block->length;
unknown's avatar
unknown committed
4078
    else
4079
      used+= block->length;
unknown's avatar
unknown committed
4080 4081 4082 4083 4084 4085
    switch(block->type) {
    case Query_cache_block::FREE:
    {
      Query_cache_memory_bin *bin = *((Query_cache_memory_bin **)
				      block->data());
      //is it correct pointer?
4086 4087
      if (((uchar*)bin) < ((uchar*)bins) ||
	  ((uchar*)bin) >= ((uchar*)first_block))
unknown's avatar
unknown committed
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098
      {
	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
      {
4099
	int idx = (((uchar*)bin) - ((uchar*)bins)) /
unknown's avatar
unknown committed
4100 4101 4102 4103 4104 4105 4106
	  sizeof(Query_cache_memory_bin);
	if (in_list(bins[idx].free_blocks, block, "free memory"))
	  result = 1;
      }
      break;
    }
    case Query_cache_block::TABLE:
4107
      if (in_list(tables_blocks, block, "tables"))
unknown's avatar
unknown committed
4108
	result = 1;
unknown's avatar
unknown committed
4109 4110
      if (in_table_list(block->table(0),  block->table(0), "table list root"))
	result = 1;
unknown's avatar
unknown committed
4111 4112
      break;
    case Query_cache_block::QUERY:
unknown's avatar
unknown committed
4113
    {
unknown's avatar
unknown committed
4114 4115
      if (in_list(queries_blocks, block, "query"))
	result = 1;
unknown's avatar
unknown committed
4116 4117 4118 4119 4120
      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 *) 
4121
	  (((uchar*)block_table->parent) -
unknown's avatar
unknown committed
4122 4123 4124 4125 4126
	   ALIGN_SIZE(sizeof(Query_cache_block_table)));
	
    	if (in_table_list(block_table, block_table_root, "table list"))
    	  result = 1;
      }
unknown's avatar
unknown committed
4127
      break;
unknown's avatar
unknown committed
4128
    }
unknown's avatar
unknown committed
4129
    case Query_cache_block::RES_INCOMPLETE:
4130 4131
      // This type of block can be not lincked yet (in multithread environment)
      break;
unknown's avatar
unknown committed
4132 4133 4134 4135 4136
    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();
4137 4138
      if (((uchar*)query_block) < ((uchar*)first_block) ||
	  ((uchar*)query_block) >= (((uchar*)first_block) + query_cache_size))
unknown's avatar
unknown committed
4139 4140 4141 4142 4143 4144
      {
	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,
4145
		    (ulong) (((uchar*)first_block) + query_cache_size)));
unknown's avatar
unknown committed
4146 4147 4148 4149
	result = 1;
      }
      else
      {
4150
	BLOCK_LOCK_RD(query_block);
unknown's avatar
unknown committed
4151 4152 4153 4154 4155
	if (in_list(queries_blocks, query_block, "query from results"))
	  result = 1;
	if (in_list(query_block->query()->result(), block,
		    "results"))
	  result = 1;
4156
	BLOCK_UNLOCK_RD(query_block);
unknown's avatar
unknown committed
4157 4158 4159 4160
      }
      break;
    }
    default:
unknown's avatar
unknown committed
4161 4162
      DBUG_PRINT("error", ("block 0x%lx have incorrect type %u",
                           (long) block, block->type));
unknown's avatar
unknown committed
4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
      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));
4192 4193 4194 4195
      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)
unknown's avatar
unknown committed
4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220
      {
	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 ..."));
4221
  if ((block = tables_blocks))
unknown's avatar
unknown committed
4222
  {
4223
    do
unknown's avatar
unknown committed
4224
    {
4225 4226
      DBUG_PRINT("qcache", ("block 0x%lx, type %u...", 
			    (ulong) block, (uint) block->type));
4227 4228 4229 4230
      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)
unknown's avatar
unknown committed
4231
      {
4232 4233 4234 4235 4236 4237 4238 4239
	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);
unknown's avatar
unknown committed
4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259
  }

  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)
      {
4260
	DBUG_PRINT("error", ("bins[%d].number= %d, but bin have %d blocks",
unknown's avatar
unknown committed
4261
			     i, bins[i].number,  count));
unknown's avatar
unknown committed
4262 4263 4264 4265 4266
	result = 1;
      }
    }
  }
  DBUG_ASSERT(result == 0);
4267
  if (!locked)
4268
    STRUCT_UNLOCK(&structure_guard_mutex);
4269
  DBUG_RETURN(result);
unknown's avatar
unknown committed
4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287
}


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
4288
      for (; block != point; block = block->pnext)
unknown's avatar
unknown committed
4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315
	    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
4316
      for (; block != point; block = block->pprev)
unknown's avatar
unknown committed
4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344
	    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
4345
      for (; block != point; block = block->next)
unknown's avatar
unknown committed
4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384
	    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;
}

unknown's avatar
unknown committed
4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418
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
4419
      for (; table != point; table = table->next)
unknown's avatar
unknown committed
4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465
	    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;
}

unknown's avatar
unknown committed
4466
#endif /* DBUG_OFF */
unknown's avatar
unknown committed
4467 4468

#endif /*HAVE_QUERY_CACHE*/