List:Commits« Previous MessageNext Message »
From:Chad MILLER Date:January 5 2007 6:31pm
Subject:bk commit into 5.0 tree (cmiller:1.2359) BUG#22821
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of cmiller. When cmiller does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-01-05 13:31:05-05:00, cmiller@stripped +17 -0
  Bug#22821: Adding "SHOW HOST_CACHE" patch from Jeremy Cole
  
  This doesn't implement "SHOW HOST_CACHE" as a command, but it
  does add the same functionality as a table in the system infor-
  mation schema.

  include/my_net.h@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +29 -3
    Use the system's gethostbyaddr_r if it exists.  If not, emulate it.

  mysql-test/r/information_schema.result@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +20 -2
    Prove new features.

  mysql-test/r/information_schema_db.result@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +1 -0
    New IS table.

  mysql-test/r/mysqlshow.result@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +2 -0
    New IS table.

  mysql-test/t/information_schema.test@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +13 -3
    Prove new features.

  mysys/Makefile.am@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +1 -0
    Add mygethostbyaddr.c

  mysys/my_thr_init.c@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +9 -0
    Add mutexes if we need to implement our own gethostbyaddr_r().

  sql/hash_filo.cc@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +139 -2
    Move implementation to here.

  sql/hash_filo.h@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +42 -85
    Move implementation from here.

  sql/hostname.cc@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +234 -142
    Improve the function names.  Add lots of comments

  sql/mysql_priv.h@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +16 -2
    Include updated hash_filo.h and the new host_entry class, and
    the prototypes of a few new functions.

  sql/mysqld.cc@stripped, 2007-01-05 13:31:01-05:00, cmiller@stripped +12 -0
    Include new system variable and status variables.

  sql/set_var.cc@stripped, 2007-01-05 13:31:02-05:00, cmiller@stripped +19 -0
    Implement setting new system variable.

  sql/set_var.h@stripped, 2007-01-05 13:31:02-05:00, cmiller@stripped +9 -0
    Implement setting new system variable.

  sql/sql_parse.cc@stripped, 2007-01-05 13:31:02-05:00, cmiller@stripped +9 -8
    Use improved function names.

  sql/sql_show.cc@stripped, 2007-01-05 13:31:02-05:00, cmiller@stripped +68 -0
    Add a HOST_CACHE table to the INFORMATION_SCHEMA database.
    
    HOST_CACHE has four columns,
      ip_address
      host_name     (the PTR name of the IP address, if available)
      errors
      hits

  sql/table.h@stripped, 2007-01-05 13:31:02-05:00, cmiller@stripped +1 -0
    Include HOST_CACHE in the information_schema enumeration.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	cmiller
# Host:	zippy.cornsilk.net
# Root:	/home/cmiller/work/mysql/mysql-5.0-community--bug22821

--- 1.17/include/my_net.h	2007-01-05 13:31:11 -05:00
+++ 1.18/include/my_net.h	2007-01-05 13:31:11 -05:00
@@ -88,13 +88,39 @@ C_MODE_START
 
 void my_inet_ntoa(struct in_addr in, char *buf);
 
+#if !defined(HPUX10)
+struct hostent;
+#endif /* HPUX */
+
+/*
+  Handling of gethostbyaddr_r()
+*/
+
+#if defined(HAVE_GETHOSTBYADDR_R)
+/* The system natively has a gethostbyaddr_r, so we won't need _free() */
+#define my_gethostbyaddr_r_free()
+#if !defined(HAVE_SOLARIS_STYLE_GETHOST)
+struct hostent *my_gethostbyaddr_r(const char *addr,
+                                   int length, int format,
+				                           struct hostent *result, char *buffer,
+				                           int buflen, int *h_errnop);
+#else
+/* Solaris-style gethostbyaddr_r, so macro it */
+#define my_gethostbyaddr_r(A,B,C,D,E,F,G) gethostbyaddr_r((A),(B),(C),(D),(E),(F),(G))
+#endif
+#else
+/* No gethostbyaddr_r available at all, so emulate it using mutex */
+struct hostent *my_gethostbyaddr_r(const char *addr,
+                                   int length, int format,
+				                           struct hostent *result, char *buffer,
+				                           int buflen, int *h_errnop);
+void my_gethostbyaddr_r_free();
+#endif
+
 /*
   Handling of gethostbyname_r()
 */
 
-#if !defined(HPUX10)
-struct hostent;
-#endif /* HPUX */
 #if !defined(HAVE_GETHOSTBYNAME_R)
 struct hostent *my_gethostbyname_r(const char *name,
 				   struct hostent *result, char *buffer,

--- 1.74/mysys/Makefile.am	2007-01-05 13:31:11 -05:00
+++ 1.75/mysys/Makefile.am	2007-01-05 13:31:11 -05:00
@@ -53,6 +53,7 @@ libmysys_a_SOURCES =    my_init.c my_get
 			my_net.c my_semaphore.c my_port.c my_sleep.c \
 			charset.c charset-def.c my_bitmap.c my_bit.c md5.c \
 			my_gethostbyname.c rijndael.c my_aes.c sha1.c \
+			my_gethostbyaddr.c \
 			my_handler.c my_netware.c my_largepage.c \
 			my_memmem.c \
 			my_windac.c my_access.c base64.c my_libwrap.c

--- 1.35/mysys/my_thr_init.c	2007-01-05 13:31:11 -05:00
+++ 1.36/mysys/my_thr_init.c	2007-01-05 13:31:11 -05:00
@@ -36,6 +36,9 @@ uint 		my_thread_end_wait_time= 5;
 #if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
 pthread_mutex_t LOCK_localtime_r;
 #endif
+#ifndef HAVE_GETHOSTBYADDR_R
+pthread_mutex_t LOCK_gethostbyaddr_r;
+#endif
 #ifndef HAVE_GETHOSTBYNAME_R
 pthread_mutex_t LOCK_gethostbyname_r;
 #endif
@@ -104,6 +107,9 @@ my_bool my_thread_global_init(void)
 #if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
   pthread_mutex_init(&LOCK_localtime_r,MY_MUTEX_INIT_SLOW);
 #endif
+#ifndef HAVE_GETHOSTBYADDR_R
+  pthread_mutex_init(&LOCK_gethostbyaddr_r,MY_MUTEX_INIT_SLOW);
+#endif
 #ifndef HAVE_GETHOSTBYNAME_R
   pthread_mutex_init(&LOCK_gethostbyname_r,MY_MUTEX_INIT_SLOW);
 #endif
@@ -161,6 +167,9 @@ void my_thread_global_end(void)
   }
 #if !defined(HAVE_LOCALTIME_R) || !defined(HAVE_GMTIME_R)
   pthread_mutex_destroy(&LOCK_localtime_r);
+#endif
+#ifndef HAVE_GETHOSTBYADDR_R
+  pthread_mutex_destroy(&LOCK_gethostbyaddr_r);
 #endif
 #ifndef HAVE_GETHOSTBYNAME_R
   pthread_mutex_destroy(&LOCK_gethostbyname_r);

--- 1.10/sql/hash_filo.cc	2007-01-05 13:31:11 -05:00
+++ 1.11/sql/hash_filo.cc	2007-01-05 13:31:11 -05:00
@@ -15,8 +15,8 @@
 
 
 /*
-** A class for static sized hash tables where old entries are deleted according
-** to usage.
+  A class for statically-sized hash tables, where entries maintained in
+  least recently used order using a doubly-linked list for easy traversal.
 */
 
 #ifdef USE_PRAGMA_IMPLEMENTATION
@@ -25,3 +25,140 @@
 
 #include "mysql_priv.h"
 #include "hash_filo.h"
+
+hash_filo_metrics::hash_filo_metrics()
+{
+  reset();
+}
+
+void hash_filo_metrics::reset()
+{
+  hits= misses= inserts= prunes= 0;
+}
+
+hash_filo::hash_filo(uint size_arg, uint key_offset_arg, uint key_length_arg,
+          hash_get_key get_key_arg, hash_free_key free_element_arg,
+          CHARSET_INFO *hash_charset_arg)
+  :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
+   get_key(get_key_arg), free_element(free_element_arg), init(0),
+   hash_charset(hash_charset_arg), metrics(NULL)
+{
+  bzero((char*) &cache, sizeof(cache));
+}
+
+hash_filo::~hash_filo()
+{
+  if (init)
+  {
+    if (cache.array.buffer)	/* Avoid problems with thread library */
+      (void) hash_free(&cache);
+    pthread_mutex_destroy(&lock);
+  }
+}
+
+void hash_filo::set_metrics(hash_filo_metrics *in_metrics)
+{
+  metrics= in_metrics;
+}
+  
+void hash_filo::clear(bool locked)
+{
+  if (!init)
+  {
+    init=1;
+    (void) pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
+  }
+  if (!locked)
+    (void) pthread_mutex_lock(&lock);
+  (void) hash_free(&cache);
+  (void) hash_init(&cache, hash_charset, size, key_offset, 
+                   key_length, get_key, free_element, 0);
+  if(metrics) metrics->reset();
+  if (!locked)
+    (void) pthread_mutex_unlock(&lock);
+  newest_element_p= oldest_element_p= NULL;
+  if(metrics)
+  {
+    metrics->used= 0;
+    metrics->free= size;
+  }
+}
+
+void hash_filo::resize(uint new_size)
+{
+  (void) pthread_mutex_lock(&lock);
+  size= new_size;
+  clear(1);
+  (void) pthread_mutex_unlock(&lock);
+}
+
+hash_filo_element *hash_filo::search(gptr key, uint length)
+{
+  hash_filo_element *entry= (hash_filo_element*) hash_search(&cache,
+                                                             (byte*) key,
+                                                             length);
+  if (entry)
+  {						// Found; link it first
+    if(metrics) statistic_increment(metrics->hits, &metrics->lock);
+    if (entry != newest_element_p)
+    {						// Relink used-chain
+      if (entry == oldest_element_p)
+        oldest_element_p= entry->newer_element_p;
+      else {
+        entry->older_element_p->newer_element_p= entry->newer_element_p;
+        entry->newer_element_p->older_element_p= entry->older_element_p;
+      }
+      if ((entry->older_element_p= newest_element_p))
+        newest_element_p->newer_element_p= entry;
+      newest_element_p= entry;
+    }
+  } else {
+    if(metrics) statistic_increment(metrics->misses, &metrics->lock);
+  }
+  return entry;
+}
+
+my_bool hash_filo::add(hash_filo_element *entry)
+{
+  if(unlikely(size == 0)) {
+    /* Our HASH size is 0, so throw away any entries attempted. */
+    if(free_element)
+      (*free_element)(entry);
+    return 0;
+  }
+      
+  if (cache.records == size)
+  {
+    hash_filo_element *tmp= oldest_element_p;
+    
+    if((oldest_element_p= oldest_element_p->newer_element_p))
+      oldest_element_p->older_element_p= NULL;
+    else
+      newest_element_p = NULL;
+
+    hash_delete(&cache, (byte*) tmp);
+    if(metrics) statistic_increment(metrics->prunes, &metrics->lock);
+  }
+    
+  if (my_hash_insert(&cache, (byte*) entry))
+  {
+    if (free_element)
+      (*free_element)(entry);		// This should never happen
+    return 1;
+  }
+  if(metrics) statistic_increment(metrics->inserts, &metrics->lock);
+
+  if ((entry->older_element_p= newest_element_p))
+    newest_element_p->newer_element_p= entry;
+  else
+    oldest_element_p= entry;
+  newest_element_p= entry;
+
+  if(metrics)
+  {
+    metrics->used= cache.records;
+    metrics->free= size - cache.records;
+  }
+
+  return 0;
+}

--- 1.14/sql/hash_filo.h	2007-01-05 13:31:11 -05:00
+++ 1.15/sql/hash_filo.h	2007-01-05 13:31:11 -05:00
@@ -15,8 +15,8 @@
 
 
 /*
-** A class for static sized hash tables where old entries are deleted in
-** first-in-last-out to usage.
+  A class for statically-sized hash tables, where entries maintained in
+  least recently used order using a doubly-linked list for easy traversal.
 */
 
 #ifndef  HASH_FILO_H
@@ -28,106 +28,63 @@
 
 class hash_filo_element
 {
-  hash_filo_element *next_used,*prev_used;
+  hash_filo_element *newer_element_p, *older_element_p;
  public:
   hash_filo_element() {}
+  hash_filo_element *newer_element() { return newer_element_p; }
+  hash_filo_element *older_element() { return older_element_p; }
   friend class hash_filo;
 };
 
+class hash_filo_metrics
+{
+ public:
+  hash_filo_metrics();
+  void reset();
+
+  uint32 hits;
+  uint32 misses;
+  uint32 inserts;
+  uint32 prunes;
+
+  uint32 used, free;
+
+  pthread_mutex_t lock;
+
+  friend class hash_filo;
+};
 
 class hash_filo
 {
-  const uint size, key_offset, key_length;
+  uint size;
+  const uint key_offset, key_length;
   const hash_get_key get_key;
   hash_free_key free_element;
   bool init;
   CHARSET_INFO *hash_charset;
 
-  hash_filo_element *first_link,*last_link;
+  hash_filo_metrics *metrics;
+  hash_filo_element *newest_element_p, *oldest_element_p;
+  
 public:
   pthread_mutex_t lock;
   HASH cache;
 
   hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
 	    hash_get_key get_key_arg, hash_free_key free_element_arg,
-	    CHARSET_INFO *hash_charset_arg)
-    :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
-    get_key(get_key_arg), free_element(free_element_arg),init(0),
-    hash_charset(hash_charset_arg)
-  {
-    bzero((char*) &cache,sizeof(cache));
-  }
+            CHARSET_INFO *hash_charset_arg);
+  ~hash_filo();
 
-  ~hash_filo()
-  {
-    if (init)
-    {
-      if (cache.array.buffer)	/* Avoid problems with thread library */
-	(void) hash_free(&cache);
-      pthread_mutex_destroy(&lock);
-    }
-  }
-  void clear(bool locked=0)
-  {
-    if (!init)
-    {
-      init=1;
-      (void) pthread_mutex_init(&lock,MY_MUTEX_INIT_FAST);
-    }
-    if (!locked)
-      (void) pthread_mutex_lock(&lock);
-    (void) hash_free(&cache);
-    (void) hash_init(&cache,hash_charset,size,key_offset, 
-    		     key_length, get_key, free_element,0);
-    if (!locked)
-      (void) pthread_mutex_unlock(&lock);
-    first_link=last_link=0;
-  }
+  void set_metrics(hash_filo_metrics *in_metrics);
 
-  hash_filo_element *search(gptr key,uint length)
-  {
-    hash_filo_element *entry=(hash_filo_element*)
-      hash_search(&cache,(byte*) key,length);
-    if (entry)
-    {						// Found; link it first
-      if (entry != first_link)
-      {						// Relink used-chain
-	if (entry == last_link)
-	  last_link=entry->prev_used;
-	else
-	{
-	  entry->next_used->prev_used = entry->prev_used;
-	  entry->prev_used->next_used = entry->next_used;
-	}
-	if ((entry->next_used= first_link))
-	  first_link->prev_used=entry;
-	first_link=entry;
-      }
-    }
-    return entry;
-  }
+  hash_filo_element *newest_element() { return newest_element_p; }
+  hash_filo_element *oldest_element() { return oldest_element_p; }
 
-  my_bool add(hash_filo_element *entry)
-  {
-    if (cache.records == size)
-    {
-      hash_filo_element *tmp=last_link;
-      last_link=last_link->prev_used;
-      hash_delete(&cache,(byte*) tmp);
-    }
-    if (my_hash_insert(&cache,(byte*) entry))
-    {
-      if (free_element)
-	(*free_element)(entry);		// This should never happen
-      return 1;
-    }
-    if ((entry->next_used=first_link))
-      first_link->prev_used=entry;
-    else
-      last_link=entry;
-    first_link=entry;
-    return 0;
-  }
+  void clear(bool locked=0);
+  void resize(uint new_size);
+  
+  hash_filo_element *search(gptr key, uint length);
+  my_bool add(hash_filo_element *entry);
 };
 
 #endif

--- 1.36/sql/hostname.cc	2007-01-05 13:31:11 -05:00
+++ 1.37/sql/hostname.cc	2007-01-05 13:31:11 -05:00
@@ -15,8 +15,9 @@
 
 
 /*
-  Get hostname for an IP.  Hostnames are checked with reverse name lookup and
-  checked that they doesn't resemble an ip.
+  Get hostname for an IP.  Hostnames are then checked with forward lookup
+  to see that they match the IP address, and checked that they don't 
+  resemble an IP address.
 */
 
 #include "mysql_priv.h"
@@ -37,212 +38,287 @@ extern "C" {					// Because of SCO 3.2V4
 #endif
 
 
-class host_entry :public hash_filo_element
-{
-public:
-  char	 ip[sizeof(((struct in_addr *) 0)->s_addr)];
-  uint	 errors;
-  char	 *hostname;
-};
+hash_filo *hostname_cache;
+hash_filo_metrics hostname_cache_metrics;
 
-static hash_filo *hostname_cache;
-static pthread_mutex_t LOCK_hostname;
+/*
+  A simple inline function to return the hash key for a given entry.
+*/
+static inline byte *host_entry_get_key(host_entry *entry, uint *length,
+                                       my_bool not_used __attribute__((unused)))
+ {
+  *length= sizeof(uint32);
+  return (byte *) &entry->ip;
+}
 
-void hostname_cache_refresh()
+/*
+  A simple inline function to free a hash entry.
+*/
+static inline void host_entry_free_key(host_entry *entry)
 {
-  hostname_cache->clear();
+  my_free(entry->hostname, MYF(0));
+  my_free((byte *) entry, MYF(0));
 }
 
+
+/**
+  Initializes the hash structure for the hostname cache.  Should be
+  called while the server is running single-threaded, before accepting
+  any connections.
+*/
 bool hostname_cache_init()
 {
-  host_entry tmp;
-  uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp);
-  if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset,
-				     sizeof(struct in_addr),NULL,
-				     (hash_free_key) free,
-				     &my_charset_bin)))
-    return 1;
+  DBUG_ENTER("hostname_cache_init");
+  if (!(hostname_cache= new hash_filo(host_cache_size, 0, 0,
+                                      (hash_get_key) host_entry_get_key,
+                                      (hash_free_key) host_entry_free_key,
+                                      &my_charset_latin1)))
+    DBUG_RETURN(1);
   hostname_cache->clear();
-  (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW);
-  return 0;
+  hostname_cache->set_metrics(&hostname_cache_metrics);
+  DBUG_RETURN(0);
 }
 
+/**
+  Cleans up the hash structure for the hostname cache.  Should be called
+  after stopping accepting connections, before shutting down.
+*/
 void hostname_cache_free()
 {
+  DBUG_ENTER("hostname_cache_free");
   if (hostname_cache)
   {
-    (void) pthread_mutex_destroy(&LOCK_hostname);
     delete hostname_cache;
     hostname_cache= 0;
   }
+  DBUG_VOID_RETURN;
+}
+
+/**
+  Empty the contents of the hostname cache.
+*/
+void hostname_cache_refresh()
+{
+  DBUG_ENTER("hostname_cache_refresh");
+  hostname_cache->clear();
+  DBUG_VOID_RETURN;
+}
+
+/**
+  Resize the hostname cache by resizing the hash table underlying it.
+
+  @param  size  new size of the hostname cache
+*/
+void hostname_cache_resize(uint size)
+{
+  hostname_cache->resize(size);
 }
 
 
-static void add_hostname(struct in_addr *in,const char *name)
+/**
+  Adds an entry in the hostname cache for an IP address stored in 'in'
+  and a hostname 'name'.
+
+  @param  in    pointer to struct in_addr with IP address
+  @param  name  string with hostname associated to IP address
+*/
+static void hostname_cache_add(struct in_addr *in, const char *name)
 {
-  if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+  DBUG_ENTER("hostname_cache_add");
+ 
+  if (likely(host_cache_size > 0)) 
   {
     VOID(pthread_mutex_lock(&hostname_cache->lock));
     host_entry *entry;
     if (!(entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
     {
-      uint length=name ? (uint) strlen(name) : 0;
-
-      if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
+      if ((entry= (host_entry*) my_malloc(sizeof(host_entry),
+                                          MYF(MY_WME|MY_ZEROFILL))))
       {
-	char *new_name;
-	memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr));
-	if (length)
-	  memcpy(new_name= (char *) (entry+1), name, length+1);
-	else
-	  new_name=0;
-	entry->hostname=new_name;
-	entry->errors=0;
+        entry->ip = (uint32)in->s_addr;
+        if (name[0] != '\0')
+          entry->hostname= my_strdup(name, MYF(0));
 	(void) hostname_cache->add(entry);
       }
     }
     VOID(pthread_mutex_unlock(&hostname_cache->lock));
   }
+  DBUG_VOID_RETURN;
 }
 
+/**
+  Add a denied IP address to the hostname cache, so it can be quickly
+  denied in the future.
 
-inline void add_wrong_ip(struct in_addr *in)
+  @param  in  pointer to struct in_addr with IP address    
+*/
+inline void hostname_cache_add_deny(struct in_addr *in)
 {
-  add_hostname(in,NullS);
+  DBUG_ENTER("hostname_cache_add_deny");
+  hostname_cache_add(in, NullS);
+  DBUG_VOID_RETURN;
 }
 
-void inc_host_errors(struct in_addr *in)
+/**
+  Increment the error count for an IP address.
+  
+  @param  in  pointer to struct in_addr with IP address
+*/
+void hostname_cache_inc_errors(struct in_addr *in)
 {
+  DBUG_ENTER("hostname_cache_inc_errors");
+ 
   VOID(pthread_mutex_lock(&hostname_cache->lock));
   host_entry *entry;
   if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
     entry->errors++;
   VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ 
+  DBUG_VOID_RETURN;
 }
 
-void reset_host_errors(struct in_addr *in)
+/**
+  Reset the error count for an IP address.
+  
+  @param  in  pointer to struct in_addr with IP address
+*/
+void hostname_cache_reset_errors(struct in_addr *in)
 {
+  DBUG_ENTER("hostname_cache_reset_errors");
+ 
   VOID(pthread_mutex_lock(&hostname_cache->lock));
   host_entry *entry;
   if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
     entry->errors=0;
   VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ 
+  DBUG_VOID_RETURN;
 }
 
+
 /* Deal with systems that don't defined INADDR_LOOPBACK */
 #ifndef INADDR_LOOPBACK
 #define INADDR_LOOPBACK 0x7f000001UL
 #endif
 
+/**
+  Double reverse resolve an IP address and return the hostname and
+  a count of errors seen from this IP address.  Cache successes
+  and failures using hostname_cache API.
+ 
+  This is called before accepting a connection, so that MySQL has 
+  access to the hostname for checking GRANTs.
+
+  The results are kept in a cache (hostname_cache) for future lookups.
+     
+  @param  in      struct in_addr with IP address to resolve
+  @param  errors  pointer to uint to fill in with count of errors
+   
+  @return hostname if successful, else NULL
+*/
+
 my_string ip_to_hostname(struct in_addr *in, uint *errors)
 {
-  uint i;
-  host_entry *entry;
+  char *name;
   DBUG_ENTER("ip_to_hostname");
   *errors=0;
 
-  /* We always treat the loopback address as "localhost". */
+  /*
+    We always treat the loopback address as "localhost".
+  */
   if (in->s_addr == htonl(INADDR_LOOPBACK))   // is expanded inline by gcc
     DBUG_RETURN((char *)my_localhost);
 
-  /* Check first if we have name in cache */
-  if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+  /*
+    Check the hostname cache for this IP address first.
+  */
+  if (likely(host_cache_size > 0))
   {
+    host_entry *entry;
     VOID(pthread_mutex_lock(&hostname_cache->lock));
     if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
     {
-      char *name;
       if (!entry->hostname)
 	name=0;					// Don't allow connection
       else
 	name=my_strdup(entry->hostname,MYF(0));
       *errors= entry->errors;
+      entry->hits++;
       VOID(pthread_mutex_unlock(&hostname_cache->lock));
       DBUG_RETURN(name);
     }
     VOID(pthread_mutex_unlock(&hostname_cache->lock));
   }
 
-  struct hostent *hp, *check;
-  char *name;
-  LINT_INIT(check);
-#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
-  char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE];
+  /*
+    The IP didn't exist in the hostname cache, we need to resolve it.
+  */
+
+  struct hostent *reverse, *forward;
+  struct hostent he_reverse, he_forward;
+  char buf_reverse[GETHOSTBYADDR_BUFF_SIZE];
+  char buf_forward[GETHOSTBYNAME_BUFF_SIZE];
   int tmp_errno;
-  struct hostent tmp_hostent, tmp_hostent2;
-#ifdef HAVE_purify
-  bzero(buff,sizeof(buff));		// Bug in purify
-#endif
-  if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in),
-			   AF_INET,
-			   &tmp_hostent,buff,sizeof(buff),&tmp_errno)))
-  {
-    DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno));
-    return 0;
-  }
-  if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2),
-				 &tmp_errno)))
-  {
-    DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
+
     /*
-      Don't cache responses when the DSN server is down, as otherwise
-      transient DNS failure may leave any number of clients (those
-      that attempted to connect during the outage) unable to connect
-      indefinitely.
+    Reverse resolve the IP address.
     */
-    if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
-      add_wrong_ip(in);
-    my_gethostbyname_r_free();
-    DBUG_RETURN(0);
-  }
-  if (!hp->h_name[0])
-  {
-    DBUG_PRINT("error",("Got an empty hostname"));
-    add_wrong_ip(in);
-    my_gethostbyname_r_free();
-    DBUG_RETURN(0);				// Don't allow empty hostnames
-  }
-  if (!(name=my_strdup(hp->h_name,MYF(0))))
+  if (!(reverse= my_gethostbyaddr_r((char*) in, sizeof(*in),
+                                    AF_INET, &he_reverse, 
+                                    buf_reverse, sizeof(buf_reverse),
+                                    &tmp_errno)))
   {
-    my_gethostbyname_r_free();
-    DBUG_RETURN(0);				// out of memory
-  }
-  my_gethostbyname_r_free();
-#else
-  VOID(pthread_mutex_lock(&LOCK_hostname));
-  if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
-  {
-    VOID(pthread_mutex_unlock(&LOCK_hostname));
-    DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
-
-    if (errno == HOST_NOT_FOUND || errno == NO_DATA)
-      goto add_wrong_ip_and_return;
-    /* Failure, don't cache responce */
+    DBUG_PRINT("error", ("In reverse lookup: my_gethostbyaddr_r returned %d",
+                         tmp_errno));
+    my_gethostbyaddr_r_free();
     DBUG_RETURN(0);
   }
-  if (!hp->h_name[0])				// Don't allow empty hostnames
+
+  /*
+    Check that we got some hostname back, fail if we got an empty one.
+  */
+  if (!reverse->h_name[0])
   {
-    VOID(pthread_mutex_unlock(&LOCK_hostname));
-    DBUG_PRINT("error",("Got an empty hostname"));
-    goto add_wrong_ip_and_return;
+    DBUG_PRINT("error", ("Got an empty hostname from reverse lookup"));
+    goto cache_failure_and_return;
   }
-  if (!(name=my_strdup(hp->h_name,MYF(0))))
+  
+  /*
+    Copy the hostname we got back, so that we can call my_gethostbyaddr_r_free.
+  */
+  if (!(name= my_strdup(reverse->h_name, MYF(0))))
   {
-    VOID(pthread_mutex_unlock(&LOCK_hostname));
+    my_gethostbyaddr_r_free();
     DBUG_RETURN(0);				// out of memory
   }
-  check=gethostbyname(name);
-  VOID(pthread_mutex_unlock(&LOCK_hostname));
-  if (!check)
+  my_gethostbyaddr_r_free();
+
+  /*
+    Forward resolve the hostname we got from the above reverse resolve.
+  */
+  if (!(forward= my_gethostbyname_r(reverse->h_name, &he_forward,
+                                    buf_forward, sizeof(buf_forward),
+                                    &tmp_errno)))
   {
-    DBUG_PRINT("error",("gethostbyname returned %d",errno));
+    DBUG_PRINT("error", ("In forward lookup: my_gethostbyname_r returned %d",
+                         tmp_errno));
+    /*
+      Don't cache responses when the DNS server is down, as otherwise
+      transient DNS failure may leave any number of clients (those
+      that attempted to connect during the outage) unable to connect
+      indefinitely.
+    */
+    if (tmp_errno == HOST_NOT_FOUND || tmp_errno == NO_DATA)
+      hostname_cache_add_deny(in);
     my_free(name,MYF(0));
+    my_gethostbyname_r_free();
     DBUG_RETURN(0);
   }
-#endif
 
-  /* Don't accept hostnames that starts with digits because they may be
-     false ip:s */
+  /*
+    Don't accept hostnames that start with digits because they may be
+    false IP addresses.
+  */
   if (my_isdigit(&my_charset_latin1,name[0]))
   {
     char *pos;
@@ -250,24 +326,40 @@ my_string ip_to_hostname(struct in_addr 
     if (*pos == '.')
     {
       DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
-      my_free(name,MYF(0));
-      goto add_wrong_ip_and_return;
+      goto cache_failure_and_return;
     }
   }
 
-  /* Check that 'gethostbyname' returned the used ip */
-  for (i=0; check->h_addr_list[i]; i++)
+  /*
+    Check that the IP is contained in the list of returned IPs from
+    the forward resolve above.  If so, cache it so that we don't have
+    to resolve it again in the future.  If not, the connection will be
+    denied after finishing this loop.
+  */
+  for (uint i=0; forward->h_addr_list[i]; i++)
+  {
+    if (*(uint32*)(forward->h_addr_list)[i] == in->s_addr)
   {
-    if (*(uint32*)(check->h_addr_list)[i] == in->s_addr)
+      if(likely(host_cache_size > 0))
     {
-      add_hostname(in,name);
+        hostname_cache_add(in, name);
+        /* hostname_cache_add calls ::search, which will increment misses */
+        statistic_decrement(hostname_cache_metrics.misses, &hostname_cache_metrics.lock);
+      }
+      my_gethostbyname_r_free();
       DBUG_RETURN(name);
     }
   }
-  DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname"));
-  my_free(name,MYF(0));
 
-add_wrong_ip_and_return:
-  add_wrong_ip(in);
+  /*
+    Something about this IP address is wrong, and the connection will
+    be denied.  In addition, we'll cache this result, so that we can
+    deny future connections from the same IP address much faster.
+  */
+ cache_failure_and_return:
+  DBUG_PRINT("error", ("Double reverse DNS lookup failed for IP, connection will be denied."));
+  my_gethostbyname_r_free();
+  my_free(name, MYF(0));
+  hostname_cache_add_deny(in);
   DBUG_RETURN(0);
 }

--- 1.427/sql/mysql_priv.h	2007-01-05 13:31:11 -05:00
+++ 1.428/sql/mysql_priv.h	2007-01-05 13:31:11 -05:00
@@ -34,6 +34,7 @@
 #include <my_base.h>			/* Needed by field.h */
 #include "sql_bitmap.h"
 #include "sql_array.h"
+#include "hash_filo.h"
 
 #ifdef __EMX__
 #undef write  /* remove pthread.h macro definition for EMX */
@@ -1239,6 +1240,7 @@ extern ulong max_binlog_size, max_relay_
 extern ulong rpl_recovery_rank, thread_cache_size;
 extern ulong back_log;
 extern ulong specialflag, current_pid;
+extern ulong host_cache_size;
 extern ulong expire_logs_days, sync_binlog_period, sync_binlog_counter;
 extern ulong opt_tc_log_size, tc_log_max_pages_used, tc_log_page_size;
 extern ulong tc_log_page_waits;
@@ -1513,11 +1515,23 @@ bool get_field(MEM_ROOT *mem, Field *fie
 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
 
 /* from hostname.cc */
+class host_entry :public hash_filo_element
+{
+public:
+  uint32 ip;  /* IPv4 */
+  uint32 errors;
+  uint32 hits;
+  char	 *hostname;
+};
+extern hash_filo *hostname_cache;
+extern hash_filo_metrics hostname_cache_metrics;
+
 struct in_addr;
 my_string ip_to_hostname(struct in_addr *in,uint *errors);
-void inc_host_errors(struct in_addr *in);
-void reset_host_errors(struct in_addr *in);
+void hostname_cache_inc_errors(struct in_addr *in);
+void hostname_cache_reset_errors(struct in_addr *in);
 bool hostname_cache_init();
+void hostname_cache_resize(uint size);
 void hostname_cache_free();
 void hostname_cache_refresh(void);
 

--- 1.584/sql/mysqld.cc	2007-01-05 13:31:11 -05:00
+++ 1.585/sql/mysqld.cc	2007-01-05 13:31:11 -05:00
@@ -389,6 +389,7 @@ uint lower_case_table_names;
 uint tc_heuristic_recover= 0;
 uint volatile thread_count, thread_running;
 ulonglong thd_startup_options;
+ulong host_cache_size;
 ulong back_log, connect_timeout, concurrency, server_id;
 ulong table_cache_size, thread_stack, what_to_log;
 ulong query_buff_size, slow_launch_time, slave_open_temp_tables;
@@ -4625,6 +4626,7 @@ enum options_mysqld
   OPT_SLAVE_TRANS_RETRIES, OPT_READONLY, OPT_DEBUGGING,
   OPT_SORT_BUFFER, OPT_TABLE_CACHE,
   OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE,
+  OPT_HOST_CACHE_SIZE,
   OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK,
   OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS,
   OPT_INNODB_MIRRORED_LOG_GROUPS,
@@ -6000,6 +6002,10 @@ The minimum value for this variable is 4
    "How many threads we should keep in a cache for reuse.",
    (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG,
    REQUIRED_ARG, 0, 0, 16384, 0, 1, 0},
+  {"host_cache_size", OPT_HOST_CACHE_SIZE,
+   "How many hostnames should be cached to avoid resolving.",
+   (gptr*) &host_cache_size, (gptr*) &host_cache_size, 0, GET_ULONG,
+   REQUIRED_ARG, HOST_CACHE_SIZE, 0, 2048, 0, 1, 0},
   {"thread_concurrency", OPT_THREAD_CONCURRENCY,
    "Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.",
    (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG,
@@ -6180,6 +6186,12 @@ struct show_var_st status_vars[]= {
   {"Handler_savepoint_rollback",(char*) offsetof(STATUS_VAR, ha_savepoint_rollback_count), SHOW_LONG_STATUS},
   {"Handler_update",           (char*) offsetof(STATUS_VAR, ha_update_count), SHOW_LONG_STATUS},
   {"Handler_write",            (char*) offsetof(STATUS_VAR, ha_write_count), SHOW_LONG_STATUS},
+  {"Host_cache_free",          (char*) &hostname_cache_metrics.free, SHOW_LONG},
+  {"Host_cache_hits",          (char*) &hostname_cache_metrics.hits, SHOW_LONG},
+  {"Host_cache_inserts",       (char*) &hostname_cache_metrics.inserts, SHOW_LONG},
+  {"Host_cache_misses",        (char*) &hostname_cache_metrics.misses, SHOW_LONG},
+  {"Host_cache_prunes",        (char*) &hostname_cache_metrics.prunes, SHOW_LONG},
+  {"Host_cache_used",          (char*) &hostname_cache_metrics.used, SHOW_LONG},
 #ifdef HAVE_INNOBASE_DB
   {"Innodb_",                  (char*) &innodb_status_variables, SHOW_VARS},
 #endif /*HAVE_INNOBASE_DB*/

--- 1.598/sql/sql_parse.cc	2007-01-05 13:31:11 -05:00
+++ 1.599/sql/sql_parse.cc	2007-01-05 13:31:11 -05:00
@@ -365,7 +365,7 @@ int check_user(THD *thd, enum enum_serve
     if (send_old_password_request(thd) ||
         my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
     {
-      inc_host_errors(&thd->remote.sin_addr);
+      hostname_cache_inc_errors(&thd->remote.sin_addr);
       DBUG_RETURN(ER_HANDSHAKE_ERROR);
     }
     /* Final attempt to check the user based on reply */
@@ -924,7 +924,7 @@ static int check_connection(THD *thd)
 	(pkt_len= my_net_read(net)) == packet_error ||
 	pkt_len < MIN_HANDSHAKE_SIZE)
     {
-      inc_host_errors(&thd->remote.sin_addr);
+      hostname_cache_inc_errors(&thd->remote.sin_addr);
       return(ER_HANDSHAKE_ERROR);
     }
   }
@@ -932,7 +932,7 @@ static int check_connection(THD *thd)
 #include "_cust_sql_parse.h"
 #endif
   if (connect_errors)
-    reset_host_errors(&thd->remote.sin_addr);
+    hostname_cache_reset_errors(&thd->remote.sin_addr);
   if (thd->packet.alloc(thd->variables.net_buffer_length))
     return(ER_OUT_OF_RESOURCES);
 
@@ -961,14 +961,14 @@ static int check_connection(THD *thd)
     /* Do the SSL layering. */
     if (!ssl_acceptor_fd)
     {
-      inc_host_errors(&thd->remote.sin_addr);
+      hostname_cache_inc_errors(&thd->remote.sin_addr);
       return(ER_HANDSHAKE_ERROR);
     }
     DBUG_PRINT("info", ("IO layer change in progress..."));
     if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout))
     {
       DBUG_PRINT("error", ("Failed to accept new SSL connection"));
-      inc_host_errors(&thd->remote.sin_addr);
+      hostname_cache_inc_errors(&thd->remote.sin_addr);
       return(ER_HANDSHAKE_ERROR);
     }
     DBUG_PRINT("info", ("Reading user information over SSL layer"));
@@ -977,7 +977,7 @@ static int check_connection(THD *thd)
     {
       DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
 			   pkt_len));
-      inc_host_errors(&thd->remote.sin_addr);
+      hostname_cache_inc_errors(&thd->remote.sin_addr);
       return(ER_HANDSHAKE_ERROR);
     }
   }
@@ -985,7 +985,7 @@ static int check_connection(THD *thd)
 
   if (end >= (char*) net->read_pos+ pkt_len +2)
   {
-    inc_host_errors(&thd->remote.sin_addr);
+    hostname_cache_inc_errors(&thd->remote.sin_addr);
     return(ER_HANDSHAKE_ERROR);
   }
 
@@ -1017,7 +1017,7 @@ static int check_connection(THD *thd)
 
   if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
   {
-    inc_host_errors(&thd->remote.sin_addr);
+    hostname_cache_inc_errors(&thd->remote.sin_addr);
     return ER_HANDSHAKE_ERROR;
   }
 
@@ -2305,6 +2305,7 @@ int prepare_schema_table(THD *thd, LEX *
   case SCH_COLUMN_PRIVILEGES:
   case SCH_TABLE_CONSTRAINTS:
   case SCH_KEY_COLUMN_USAGE:
+  case SCH_HOST_CACHE:
   default:
     break;
   }

--- 1.335/sql/sql_show.cc	2007-01-05 13:31:11 -05:00
+++ 1.336/sql/sql_show.cc	2007-01-05 13:31:11 -05:00
@@ -24,6 +24,11 @@
 #include "sql_trigger.h"
 #include <my_dir.h>
 
+#define IP_ADDRESS_MAXLEN 39  /* num chars to represent IPv6 address, w/o NUL */
+#ifndef MAXHOSTNAMELEN
+#  define MAXHOSTNAMELEN 255
+#endif
+
 #ifdef HAVE_BERKELEY_DB
 #include "ha_berkeley.h"			// For berkeley_show_logs
 #endif
@@ -47,6 +52,57 @@ view_store_create_info(THD *thd, TABLE_L
 static bool schema_table_store_record(THD *thd, TABLE *table);
 
 
+/**
+  Fill the passed information_schema table with host cache information.
+
+  @param  thd     Ubiquitous thread object.
+  @param  tables  The destination of the host-cache info.
+  @param  cond    (Unused.)
+*/
+int fill_host_cache_i_s(THD *thd, struct st_table_list *tables, COND *cond)
+{
+  DBUG_ENTER("fill_host_cache_i_s");
+  int ret= 0;
+
+  const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
+  TABLE *table= tables->table;
+  CHARSET_INFO *cs= system_charset_info;
+
+  (void) pthread_mutex_lock(&hostname_cache->lock);
+
+  char ip[IP_ADDRESS_MAXLEN+1];
+  host_entry *cur;
+
+  for (cur= (host_entry *) hostname_cache->newest_element();
+      cur != NULL; cur= (host_entry *) cur->older_element())
+  {
+    uint32 nip = htonl(cur->ip);  /* the address, for IPv4 */
+    sprintf(ip, "%u.%u.%u.%u",
+            (unsigned char)(nip>>24), (unsigned char)(nip>>16), 
+            (unsigned char)(nip>> 8), (unsigned char)(nip));
+
+    table->field[0]->store((char *) ip, strlen(ip), system_charset_info);
+    if(cur->hostname)
+      table->field[1]->store(cur->hostname, strlen(cur->hostname), system_charset_info);
+    else
+      table->field[1]->store(NULL, 0, system_charset_info);
+    table->field[2]->store((longlong)cur->errors, TRUE);
+    table->field[3]->store((longlong)cur->hits, TRUE);
+
+    if (schema_table_store_record(thd, table))
+    {
+      ret= 1;
+      goto clean_up;
+    }
+  }
+
+clean_up:
+  (void) pthread_mutex_unlock(&hostname_cache->lock);
+
+  DBUG_RETURN(ret);
+}
+
+
 /***************************************************************************
 ** List all table types supported 
 ***************************************************************************/
@@ -4091,6 +4147,16 @@ ST_FIELD_INFO coll_charset_app_fields_in
 };
 
 
+ST_FIELD_INFO host_cache_info[]=
+{
+  {"IP_ADDRESS", IP_ADDRESS_MAXLEN, MYSQL_TYPE_STRING, 0, 0, NULL},
+  {"HOST_NAME", MAXHOSTNAMELEN, MYSQL_TYPE_STRING, 0, 1, NULL},
+  {"ERRORS", 10, MYSQL_TYPE_LONG, 0, 0, NULL},
+  {"HITS", 10, MYSQL_TYPE_LONG, 0, 0, NULL},
+  {NULL, 0, MYSQL_TYPE_STRING, 0, 0, NULL}
+};
+
+
 ST_FIELD_INFO proc_fields_info[]=
 {
   {"SPECIFIC_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
@@ -4297,6 +4363,8 @@ ST_SCHEMA_TABLE schema_tables[]=
    get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0},
   {"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
     fill_schema_column_privileges, 0, 0, -1, -1, 0},
+  {"HOST_CACHE", host_cache_info, create_schema_table,
+    fill_host_cache_i_s, 0, 0, -1, -1, 0},
   {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
     get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0},
   {"OPEN_TABLES", open_tables_fields_info, create_schema_table,

--- 1.137/sql/table.h	2007-01-05 13:31:11 -05:00
+++ 1.138/sql/table.h	2007-01-05 13:31:11 -05:00
@@ -306,6 +306,7 @@ enum enum_schema_tables
   SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
   SCH_COLUMNS,
   SCH_COLUMN_PRIVILEGES,
+  SCH_HOST_CACHE,
   SCH_KEY_COLUMN_USAGE,
   SCH_OPEN_TABLES,
   SCH_PROCEDURES,

--- 1.117/mysql-test/r/information_schema.result	2007-01-05 13:31:11 -05:00
+++ 1.118/mysql-test/r/information_schema.result	2007-01-05 13:31:11 -05:00
@@ -41,6 +41,7 @@ COLLATIONS
 COLLATION_CHARACTER_SET_APPLICABILITY
 COLUMNS
 COLUMN_PRIVILEGES
+HOST_CACHE
 KEY_COLUMN_USAGE
 ROUTINES
 SCHEMATA
@@ -730,7 +731,7 @@ CREATE TABLE t_crashme ( f1 BIGINT);
 CREATE VIEW a1 (t_CRASHME) AS SELECT f1 FROM t_crashme GROUP BY f1;
 CREATE VIEW a2 AS SELECT t_CRASHME FROM a1;
 count(*)
-101
+102
 drop view a2, a1;
 drop table t_crashme;
 select table_schema,table_name, column_name from
@@ -801,7 +802,7 @@ delete from mysql.db where user='mysqlte
 flush privileges;
 SELECT table_schema, count(*) FROM information_schema.TABLES GROUP BY TABLE_SCHEMA;
 table_schema	count(*)
-information_schema	16
+information_schema	17
 mysql	17
 create table t1 (i int, j int);
 create trigger trg1 before insert on t1 for each row
@@ -1191,6 +1192,7 @@ COLLATIONS	COLLATION_NAME
 COLLATION_CHARACTER_SET_APPLICABILITY	COLLATION_NAME
 COLUMNS	TABLE_SCHEMA
 COLUMN_PRIVILEGES	TABLE_SCHEMA
+HOST_CACHE	IP_ADDRESS
 KEY_COLUMN_USAGE	CONSTRAINT_SCHEMA
 ROUTINES	ROUTINE_SCHEMA
 SCHEMATA	SCHEMA_NAME
@@ -1222,6 +1224,7 @@ COLLATIONS	COLLATION_NAME
 COLLATION_CHARACTER_SET_APPLICABILITY	COLLATION_NAME
 COLUMNS	TABLE_SCHEMA
 COLUMN_PRIVILEGES	TABLE_SCHEMA
+HOST_CACHE	IP_ADDRESS
 KEY_COLUMN_USAGE	CONSTRAINT_SCHEMA
 ROUTINES	ROUTINE_SCHEMA
 SCHEMATA	SCHEMA_NAME
@@ -1269,3 +1272,18 @@ id	select_type	table	type	possible_keys	
 1	PRIMARY	<derived2>	system	NULL	NULL	NULL	NULL	0	const row not found
 2	DERIVED	tables	ALL	NULL	NULL	NULL	NULL	2	
 drop view v1;
+show host cache;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'host cache' at line 1
+select * from information_schema.host_cache;
+IP_ADDRESS	HOST_NAME	ERRORS	HITS
+show variables like 'Host_cache_%';
+Variable_name	Value
+host_cache_size	(configurable)
+show status like 'Host_cache_%';
+Variable_name	Value
+Host_cache_free	0
+Host_cache_hits	0
+Host_cache_inserts	0
+Host_cache_misses	0
+Host_cache_prunes	0
+Host_cache_used	0

--- 1.88/mysql-test/t/information_schema.test	2007-01-05 13:31:11 -05:00
+++ 1.89/mysql-test/t/information_schema.test	2007-01-05 13:31:11 -05:00
@@ -971,9 +971,6 @@ SELECT COLUMN_NAME, MD5(COLUMN_DEFAULT),
 DROP TABLE bug23037;
 DROP FUNCTION get_value;
 
-
-
-
 #
 # Bug#22413: EXPLAIN SELECT FROM view with ORDER BY yield server crash
 #
@@ -986,5 +983,18 @@ order by object_schema;
 explain select * from v1;
 explain select * from (select table_name from information_schema.tables) as a;
 drop view v1;
+
+#
+# Bug#22821: Adding "SHOW HOST_CACHE" patch from Jeremy Cole
+#
+###  Refuse proposed "SHOW" command.
+--error ER_PARSE_ERROR
+show host cache;
+###  Instead use information schema.
+select * from information_schema.host_cache;
+
+--replace_column 2 (configurable)
+show variables like 'Host_cache_%';
+show status like 'Host_cache_%';
 
 # End of 5.0 tests.

--- 1.9/mysql-test/r/information_schema_db.result	2007-01-05 13:31:11 -05:00
+++ 1.10/mysql-test/r/information_schema_db.result	2007-01-05 13:31:11 -05:00
@@ -10,6 +10,7 @@ COLLATIONS
 COLLATION_CHARACTER_SET_APPLICABILITY
 COLUMNS
 COLUMN_PRIVILEGES
+HOST_CACHE
 KEY_COLUMN_USAGE
 ROUTINES
 SCHEMATA

--- 1.4/mysql-test/r/mysqlshow.result	2007-01-05 13:31:11 -05:00
+++ 1.5/mysql-test/r/mysqlshow.result	2007-01-05 13:31:11 -05:00
@@ -84,6 +84,7 @@ Database: information_schema
 | COLLATION_CHARACTER_SET_APPLICABILITY |
 | COLUMNS                               |
 | COLUMN_PRIVILEGES                     |
+| HOST_CACHE                            |
 | KEY_COLUMN_USAGE                      |
 | ROUTINES                              |
 | SCHEMATA                              |
@@ -105,6 +106,7 @@ Database: INFORMATION_SCHEMA
 | COLLATION_CHARACTER_SET_APPLICABILITY |
 | COLUMNS                               |
 | COLUMN_PRIVILEGES                     |
+| HOST_CACHE                            |
 | KEY_COLUMN_USAGE                      |
 | ROUTINES                              |
 | SCHEMATA                              |

--- 1.175/sql/set_var.cc	2007-01-05 13:31:11 -05:00
+++ 1.176/sql/set_var.cc	2007-01-05 13:31:11 -05:00
@@ -409,6 +409,8 @@ sys_var_long_ptr	sys_table_lock_wait_tim
                                                     &table_lock_wait_timeout);
 sys_var_long_ptr	sys_thread_cache_size("thread_cache_size",
 					      &thread_cache_size);
+sys_var_host_cache_size sys_host_cache_size("host_cache_size",
+                                            &host_cache_size);
 sys_var_thd_enum	sys_tx_isolation("tx_isolation",
 					 &SV::tx_isolation,
 					 &tx_isolation_typelib,
@@ -640,6 +642,7 @@ sys_var *sys_variables[]=
   &sys_foreign_key_checks,
   &sys_group_concat_max_len,
   &sys_have_innodb,
+  &sys_host_cache_size,
   &sys_identity,
   &sys_init_connect,
   &sys_init_slave,
@@ -871,6 +874,7 @@ struct show_var_st init_vars[]= {
   {"have_raid",		      (char*) &have_raid,		    SHOW_HAVE},
   {"have_rtree_keys",         (char*) &have_rtree_keys,             SHOW_HAVE},
   {"have_symlink",            (char*) &have_symlink,                SHOW_HAVE},
+  {sys_host_cache_size.name,  (char*) &sys_host_cache_size,         SHOW_SYS},
   {"init_connect",            (char*) &sys_init_connect,            SHOW_SYS},
   {"init_file",               (char*) &opt_init_file,               SHOW_CHAR_PTR},
   {"init_slave",              (char*) &sys_init_slave,              SHOW_SYS},
@@ -2351,6 +2355,21 @@ void sys_var_collation_server::set_defau
  }
 }
 
+bool sys_var_host_cache_size::update(THD *thd, set_var *var)
+{
+  int new_size= var->value->val_int();
+  
+  if(new_size < 0 || new_size > 2048) {
+    my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
+             sys_host_cache_size.name, "bad value");
+    return -1;
+  }
+  
+  host_cache_size= (ulong) new_size;
+  hostname_cache_resize((uint)host_cache_size);
+  
+  return 0;
+}
 
 LEX_STRING default_key_cache_base= {(char *) "default", 7 };
 

--- 1.80/sql/set_var.h	2007-01-05 13:31:11 -05:00
+++ 1.81/sql/set_var.h	2007-01-05 13:31:11 -05:00
@@ -692,6 +692,15 @@ public:
 };
 
 
+class sys_var_host_cache_size :public sys_var_long_ptr
+{
+public:
+  sys_var_host_cache_size(const char *name_arg, ulong *value_ptr)
+    :sys_var_long_ptr(name_arg, value_ptr) {}
+  bool update(THD *thd, set_var *var);
+};
+
+
 class sys_var_key_buffer_size :public sys_var_key_cache_param
 {
 public:
Thread
bk commit into 5.0 tree (cmiller:1.2359) BUG#22821Chad MILLER5 Jan