List:Commits« Previous MessageNext Message »
From:Chad MILLER Date:May 23 2008 9:49pm
Subject:push into mysql-6.0 branch (chad:2657 to 2658) Bug#36345
View as plain text  
------------------------------------------------------------
revno: 2658
revision-id:chad@stripped
parent: chad@stripped
parent: mleich@stripped
committer: Chad MILLER <chad@stripped>
branch nick: mysql-6.0-bugteam
timestamp: Fri 2008-05-23 23:45:16 +0200
message:
  Merge from trunk.
modified:
  mysql-test/r/func_misc.result 
sp1f-func_misc.result-20001228015633-4sy6dzzt7xcs4ubzcxloyguc7zhougbr
  mysql-test/t/func_misc.test   
sp1f-func_misc.test-20001228015635-kayguwcdgtjnekzavvdzbsnqcdwfm36c
    ------------------------------------------------------------
    revno: 2655.1.1
    revision-id:mleich@stripped
    parent: kpettersson@stripped
    parent: mleich@stripped
    committer: First_name Last_name mleich@stripped
    branch nick: mysql-6.0-bugteam-36345
    timestamp: Fri 2008-05-23 17:52:02 +0200
    message:
      Upmerge of the fix for
         Bug#36345 Test 'func_misc' fails on RHAS3 x86_64
    modified:
      mysql-test/r/func_misc.result 
sp1f-func_misc.result-20001228015633-4sy6dzzt7xcs4ubzcxloyguc7zhougbr
      mysql-test/t/func_misc.test   
sp1f-func_misc.test-20001228015635-kayguwcdgtjnekzavvdzbsnqcdwfm36c
    ------------------------------------------------------------
    revno: 2497.227.18
    revision-id:mleich@stripped
    parent: kpettersson@stripped
    committer: First_name Last_name mleich@stripped
    branch nick: mysql-5.1-bugteam-36345
    timestamp: Fri 2008-05-23 17:23:55 +0200
    message:
      Fix for
         Bug#36345 Test 'func_misc' fails on RHAS3 x86_64
      and a second similar problem within this test found
      during experimenting.
    modified:
      mysql-test/r/func_misc.result 
sp1f-func_misc.result-20001228015633-4sy6dzzt7xcs4ubzcxloyguc7zhougbr
      mysql-test/t/func_misc.test   
sp1f-func_misc.test-20001228015635-kayguwcdgtjnekzavvdzbsnqcdwfm36c
------------------------------------------------------------
revno: 2657
revision-id:chad@stripped
parent: chad@stripped
parent: chad@stripped
committer: Chad MILLER <chad@stripped>
branch nick: mysql-6.0-bugteam
timestamp: Fri 2008-05-23 23:43:35 +0200
message:
  Merge from 6.0-trunk. Adds branch metainfo for mailer plugin.
added:
  .bzr-mysql/                    bzrmysql-20080521211852-kry0hytlir1epmpv-1
  .bzr-mysql/default.conf        default.conf-20080521211852-kry0hytlir1epmpv-2
    ------------------------------------------------------------
    revno: 2630.1.9
    revision-id:chad@stripped
    parent: sp1r-kostja@bodhi.(none)-20080522065447-59365
    parent: chad@stripped
    committer: Chad MILLER <chad@stripped>
    branch nick: mysql-6.0
    timestamp: Fri 2008-05-23 22:12:32 +0200
    message:
      Merge branch meta-info for mailer plugin from 5.1, updating version.
    added:
      .bzr-mysql/                    bzrmysql-20080521211852-kry0hytlir1epmpv-1
      .bzr-mysql/default.conf        default.conf-20080521211852-kry0hytlir1epmpv-2
    ------------------------------------------------------------
    revno: 2497.236.7
    revision-id:chad@stripped
    parent: sp1r-dfischer/mysqldev@stripped/production.mysql.com-20080521075958-12847
    parent: cmiller@stripped
    committer: Chad MILLER <chad@stripped>
    branch nick: mysql-5.1
    timestamp: Fri 2008-05-23 00:25:21 +0200
    message:
      Merge from 5.0.  Update version.
    added:
      .bzr-mysql/                    bzrmysql-20080521211852-kry0hytlir1epmpv-1
      .bzr-mysql/default.conf        default.conf-20080521211852-kry0hytlir1epmpv-2
    ------------------------------------------------------------
    revno: 1810.3729.10
    revision-id:cmiller@stripped
    parent: sp1r-gshchepa/uchum@stripped
    parent: chad@stripped
    committer: Chad Miller <cmiller@stripped>
    branch nick: mysql-5.0
    timestamp: Thu 2008-05-22 17:57:41 +0200
    message:
      Merge metainfo for mailer plugin.
    added:
      .bzr-mysql/                    bzrmysql-20080521211852-kry0hytlir1epmpv-1
      .bzr-mysql/default.conf        default.conf-20080521211852-kry0hytlir1epmpv-2
    ------------------------------------------------------------
    revno: 1616.3139.1
    revision-id:chad@stripped
    parent: sp1r-gluh@stripped/eagle.(none)-20071213103801-57865
    committer: Chad MILLER <chad@stripped>
    branch nick: mysql-4.1
    timestamp: Wed 2008-05-21 18:18:44 -0400
    message:
      Add branch metainfo for mailer plugin.
    added:
      .bzr-mysql/                    bzrmysql-20080521211852-kry0hytlir1epmpv-1
      .bzr-mysql/default.conf        default.conf-20080521211852-kry0hytlir1epmpv-2

=== added directory '.bzr-mysql'
=== added file '.bzr-mysql/default.conf'
--- a/.bzr-mysql/default.conf	1970-01-01 00:00:00 +0000
+++ b/.bzr-mysql/default.conf	2008-05-23 20:12:32 +0000
@@ -0,0 +1,4 @@
+[MYSQL]
+post_commit_to = "commits@stripped"
+post_push_to = "commits@stripped"
+tree_name = "mysql-6.0"

=== modified file 'Makefile.am'
--- a/Makefile.am	2008-04-29 04:37:22 +0000
+++ b/Makefile.am	2008-05-21 10:17:29 +0000
@@ -66,7 +66,7 @@
   test-pl test-force-pl test-full-pl test-force-full-pl test-force-pl-mem \
   test-unit test-ps test-nr test-pr test-ns test-binlog-statement \
   test-ext-funcs test-ext-rpl test-ext-partitions test-ext-jp \
-  test-ext-stress test-ext test-embedded \
+  test-ext-stress test-ext test-embedded test-reprepare \
   test-fast test-fast-cursor test-fast-view test-fast-prepare \
   test-full-qa
 
@@ -111,6 +111,11 @@
 	  echo "no program found for 'embedded' tests - skipped testing" ; \
 	fi
 
+test-reprepare:
+	cd mysql-test ; \
+	    @PERL@ ./mysql-test-run.pl $(force) $(mem) --ps-protocol \
+		--mysqld=--debug=+d,reprepare_each_statement
+
 test:	test-unit test-ns test-pr
 
 test-full:	test test-nr test-ps

=== modified file 'config/ac-macros/libevent.m4'
--- a/config/ac-macros/libevent.m4	2008-04-17 19:08:10 +0000
+++ b/config/ac-macros/libevent.m4	2008-04-28 23:56:34 +0000
@@ -22,347 +22,8 @@
   AC_DEFINE([HAVE_LIBEVENT], [1], [If we want to use libevent and have connection
pooling])
   AC_MSG_RESULT([using bundled libevent])
 
-  dnl Things that libevent needs
-  AC_CHECK_HEADERS(inttypes.h stdint.h poll.h signal.h sys/epoll.h sys/time.h \
-                   sys/queue.h sys/event.h sys/devpoll.h netinet/in6.h)
-
-if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
-	AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
-	AC_EGREP_CPP(yes,
-[
-#include <sys/queue.h>
-#ifdef TAILQ_FOREACH
- yes
-#endif
-],	[AC_MSG_RESULT(yes)
-	 AC_DEFINE(HAVE_TAILQFOREACH, 1,
-		[Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
-	AC_MSG_RESULT(no)
-	)
-fi
-
-if test "x$ac_cv_header_sys_time_h" = "xyes"; then
-	AC_MSG_CHECKING(for timeradd in sys/time.h)
-	AC_EGREP_CPP(yes,
-[
-#include <sys/time.h>
-#ifdef timeradd
- yes
-#endif
-],	[ AC_DEFINE(HAVE_TIMERADD, 1,
-		[Define if timeradd is defined in <sys/time.h>])
-	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
-)
-fi
-
-if test "x$ac_cv_header_sys_time_h" = "xyes"; then
-	AC_MSG_CHECKING(for timercmp in sys/time.h)
-	AC_EGREP_CPP(yes,
-[
-#include <sys/time.h>
-#ifdef timercmp
- yes
-#endif
-],	[ AC_DEFINE(HAVE_TIMERCMP, 1,
-		[Define if timercmp is defined in <sys/time.h>])
-	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
-)
-fi
-
-if test "x$ac_cv_header_sys_time_h" = "xyes"; then
-	AC_MSG_CHECKING(for timerclear in sys/time.h)
-	AC_EGREP_CPP(yes,
-[
-#include <sys/time.h>
-#ifdef timerclear
- yes
-#endif
-],	[ AC_DEFINE(HAVE_TIMERCLEAR, 1,
-		[Define if timerclear is defined in <sys/time.h>])
-	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
-)
-fi
-
-if test "x$ac_cv_header_sys_time_h" = "xyes"; then
-	AC_MSG_CHECKING(for timerisset in sys/time.h)
-	AC_EGREP_CPP(yes,
-[
-#include <sys/time.h>
-#ifdef timerisset
- yes
-#endif
-],	[ AC_DEFINE(HAVE_TIMERISSET, 1,
-		[Define if timerisset is defined in <sys/time.h>])
-	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
-)
-fi
-
-dnl Checks for library functions.
-AC_CHECK_FUNCS(vasprintf strsep getaddrinfo getnameinfo inet_ntop)
-
-if test "x$ac_cv_func_clock_gettime" = "xyes"; then
-   AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if clock_gettime is available in libc])
-else
-   AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define is no secure id variant is
available])
-fi
-
-AC_MSG_CHECKING(for F_SETFD in fcntl.h)
-AC_EGREP_CPP(yes,
-[
-#define _GNU_SOURCE
-#include <fcntl.h>
-#ifdef F_SETFD
-yes
-#endif
-],	[ AC_DEFINE(HAVE_SETFD, 1,
-	      [Define if F_SETFD is defined in <fcntl.h>])
-	  AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no))
-
-needsignal=no
-if test "x$ac_cv_func_select" = "xyes" ; then
-	 AC_LIBOBJ(select)
-	 needsignal=yes
-fi
-
-if test "x$ac_cv_func_poll" = "xyes" ; then
-	 AC_LIBOBJ(poll)
-	 needsignal=yes
-fi
-
-haveepoll=no
-AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
-if test "x$haveepoll" = "xyes" ; then
-	AC_DEFINE(HAVE_EPOLL, 1,
-		[Define if your system supports the epoll system calls])
-	AC_LIBOBJ(epoll)
-	needsignal=yes
-fi
-
-havedevpoll=no
-if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then
-	AC_DEFINE(HAVE_DEVPOLL, 1,
-		    [Define if /dev/poll is available])
-        AC_LIBOBJ(devpoll)
-fi
-
-havekqueue=no
-if test "x$ac_cv_header_sys_event_h" = "xyes"; then
-	AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
-	if test "x$havekqueue" = "xyes" ; then
-		AC_MSG_CHECKING(for working kqueue)
-		AC_TRY_RUN(
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/event.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int
-main(int argc, char **argv)
-{
-	int kq;
-	int n;
-	int fd[[2]];
-	struct kevent ev;
-	struct timespec ts;
-	char buf[[8000]];
-
-	if (pipe(fd) == -1)
-		exit(1);
-	if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
-		exit(1);
-
-	while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
-		;
-
-        if ((kq = kqueue()) == -1)
-		exit(1);
-
-	ev.ident = fd[[1]];
-	ev.filter = EVFILT_WRITE;
-	ev.flags = EV_ADD | EV_ENABLE;
-	n = kevent(kq, &ev, 1, NULL, 0, NULL);
-	if (n == -1)
-		exit(1);
-	
-	read(fd[[0]], buf, sizeof(buf));
-
-	ts.tv_sec = 0;
-	ts.tv_nsec = 0;
-	n = kevent(kq, NULL, 0, &ev, 1, &ts);
-	if (n == -1 || n == 0)
-		exit(1);
-
-	exit(0);
-}, [AC_MSG_RESULT(yes)
-    AC_DEFINE(HAVE_WORKING_KQUEUE, 1,
-		[Define if kqueue works correctly with pipes])
-    AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
-	fi
-fi
-
-haveepollsyscall=no
-if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
-	if test "x$haveepoll" = "xno" ; then
-		AC_MSG_CHECKING(for epoll system call)
-		AC_TRY_RUN(
-#include <stdint.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-#include <sys/epoll.h>
-#include <unistd.h>
-
-int
-epoll_create(int size)
-{
-	return (syscall(__NR_epoll_create, size));
-}
-
-int
-main(int argc, char **argv)
-{
-	int epfd;
-
-	epfd = epoll_create(256);
-	exit (epfd == -1 ? 1 : 0);
-}, [AC_MSG_RESULT(yes)
-    AC_DEFINE(HAVE_EPOLL, 1,
-	[Define if your system supports the epoll system calls])
-    needsignal=yes
-    AC_LIBOBJ(epoll_sub)
-    AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
-	fi
-fi
-
-haveeventports=no
-AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
-if test "x$haveeventports" = "xyes" ; then
-	AC_DEFINE(HAVE_EVENT_PORTS, 1,
-		[Define if your system supports event ports])
-	AC_LIBOBJ(evport)
-	needsignal=yes
-fi
-if test "x$needsignal" = "xyes" ; then
-	AC_LIBOBJ(signal)
-fi
-
-AC_TYPE_PID_T
-AC_TYPE_SIZE_T
-AC_CHECK_TYPE(uint64_t, unsigned long long)
-AC_CHECK_TYPE(uint32_t, unsigned int)
-AC_CHECK_TYPE(uint16_t, unsigned short)
-AC_CHECK_TYPE(uint8_t, unsigned char)
-AC_CHECK_TYPES([struct in6_addr], , ,
-[#ifdef WIN32
-#include <winsock2.h>
-#else
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_NETINET_IN6_H
-#include <netinet/in6.h>
-#endif])
-
-AC_MSG_CHECKING([for socklen_t])
-AC_TRY_COMPILE([
- #include <sys/types.h>
- #include <sys/socket.h>],
-  [socklen_t x;],
-  AC_MSG_RESULT([yes]),
-  [AC_MSG_RESULT([no])
-  AC_DEFINE(socklen_t, unsigned int,
-	[Define to unsigned int if you dont have it])]
-)
-
-AC_MSG_CHECKING([whether our compiler supports __func__])
-AC_TRY_COMPILE([],
- [ const char *cp = __func__; ],
- AC_MSG_RESULT([yes]),
- AC_MSG_RESULT([no])
- AC_MSG_CHECKING([whether our compiler supports __FUNCTION__])
- AC_TRY_COMPILE([],
-   [ const char *cp = __FUNCTION__; ],
-   AC_MSG_RESULT([yes])
-   AC_DEFINE(__func__, __FUNCTION__,
-         [Define to appropriate substitue if compiler doesnt have __func__]),
-   AC_MSG_RESULT([no])
-   AC_DEFINE(__func__, __FILE__,
-         [Define to appropriate substitue if compiler doesnt have __func__])))
-
-AH_VERBATIM([HAVE_TIMERADD], [
-/* Define if timeradd is defined in <sys/time.h> */
-#undef HAVE_TIMERADD
-#ifndef HAVE_TIMERADD
-#undef timersub
-#define timeradd(tvp, uvp, vvp)						\
-	do {								\
-		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
-		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;       \
-		if ((vvp)->tv_usec >= 1000000) {			\
-			(vvp)->tv_sec++;				\
-			(vvp)->tv_usec -= 1000000;			\
-		}							\
-	} while (0)
-#define	timersub(tvp, uvp, vvp)						\
-	do {								\
-		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
-		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
-		if ((vvp)->tv_usec < 0) {				\
-			(vvp)->tv_sec--;				\
-			(vvp)->tv_usec += 1000000;			\
-		}							\
-	} while (0)
-#endif /* !HAVE_TIMERADD */
-])
-
-AH_VERBATIM([HAVE_TIMERCLEAR], [
-#undef HAVE_TIMERCLEAR
-#ifndef HAVE_TIMERCLEAR
-#define	timerclear(tvp)	(tvp)->tv_sec = (tvp)->tv_usec = 0
-#endif
-])
-
-AH_VERBATIM([HAVE_TIMERCMP], [
-#undef HAVE_TIMERCMP
-#ifndef HAVE_TIMERCMP
-#undef timercmp
-#define	timercmp(tvp, uvp, cmp)						\
-	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
-	 ((tvp)->tv_usec cmp (uvp)->tv_usec) :				\
-	 ((tvp)->tv_sec cmp (uvp)->tv_sec))
-#endif
-])
-
-AH_VERBATIM([HAVE_TIMERISSET], [
-#undef HAVE_TIMERISSET
-#ifndef HAVE_TIMERISSET
-#undef timerisset
-#define	timerisset(tvp)	((tvp)->tv_sec || (tvp)->tv_usec)
-#endif
-])
-
-AH_VERBATIM([HAVE_TAILQFOREACH], [
-/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
-#undef HAVE_TAILQFOREACH
-#ifndef HAVE_TAILQFOREACH
-#define	TAILQ_FIRST(head)		((head)->tqh_first)
-#define	TAILQ_END(head)			NULL
-#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
-#define TAILQ_FOREACH(var, head, field)					\
-	for((var) = TAILQ_FIRST(head);					\
-	    (var) != TAILQ_END(head);					\
-	    (var) = TAILQ_NEXT(var, field))
-#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
-	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
-	(elm)->field.tqe_next = (listelm);				\
-	*(listelm)->field.tqe_prev = (elm);				\
-	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
-} while (0)
-#endif /* TAILQ_FOREACH */
-])
+  dnl Use builtin include to workaround path problems on older versions of aclocal.
+  builtin([include],[config/ac-macros/libevent_configure.m4])
 ])
 
 

=== added file 'config/ac-macros/libevent_configure.m4'
--- a/config/ac-macros/libevent_configure.m4	1970-01-01 00:00:00 +0000
+++ b/config/ac-macros/libevent_configure.m4	2008-04-29 18:53:07 +0000
@@ -0,0 +1,300 @@
+dnl Checks for libraries.
+AC_CHECK_LIB(socket, socket)
+AC_CHECK_LIB(resolv, inet_aton)
+AC_CHECK_LIB(rt, clock_gettime)
+AC_CHECK_LIB(nsl, inet_ntoa)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(fcntl.h stdarg.h inttypes.h stdint.h poll.h signal.h unistd.h
sys/epoll.h sys/time.h sys/queue.h sys/event.h sys/param.h sys/ioctl.h sys/select.h
sys/devpoll.h port.h netinet/in6.h sys/socket.h)
+if test "x$ac_cv_header_sys_queue_h" = "xyes"; then
+	AC_MSG_CHECKING(for TAILQ_FOREACH in sys/queue.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/queue.h>
+#ifdef TAILQ_FOREACH
+ yes
+#endif
+],	[AC_MSG_RESULT(yes)
+	 AC_DEFINE(HAVE_TAILQFOREACH, 1,
+		[Define if TAILQ_FOREACH is defined in <sys/queue.h>])],
+	AC_MSG_RESULT(no)
+	)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timeradd in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timeradd
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERADD, 1,
+		[Define if timeradd is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timercmp in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timercmp
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERCMP, 1,
+		[Define if timercmp is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timerclear in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerclear
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERCLEAR, 1,
+		[Define if timerclear is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+if test "x$ac_cv_header_sys_time_h" = "xyes"; then
+	AC_MSG_CHECKING(for timerisset in sys/time.h)
+	AC_EGREP_CPP(yes,
+[
+#include <sys/time.h>
+#ifdef timerisset
+ yes
+#endif
+],	[ AC_DEFINE(HAVE_TIMERISSET, 1,
+		[Define if timerisset is defined in <sys/time.h>])
+	  AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no)
+)
+fi
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_INLINE
+AC_HEADER_TIME
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(gettimeofday vasprintf fcntl clock_gettime strtok_r strsep getaddrinfo
getnameinfo strlcpy inet_ntop signal sigaction strtoll)
+
+AC_CHECK_SIZEOF(long)
+
+if test "x$ac_cv_func_clock_gettime" = "xyes"; then
+   AC_DEFINE(DNS_USE_CPU_CLOCK_FOR_ID, 1, [Define if clock_gettime is available in libc])
+else
+   AC_DEFINE(DNS_USE_GETTIMEOFDAY_FOR_ID, 1, [Define is no secure id variant is
available])
+fi
+
+AC_MSG_CHECKING(for F_SETFD in fcntl.h)
+AC_EGREP_CPP(yes,
+[
+#define _GNU_SOURCE
+#include <fcntl.h>
+#ifdef F_SETFD
+yes
+#endif
+],	[ AC_DEFINE(HAVE_SETFD, 1,
+	      [Define if F_SETFD is defined in <fcntl.h>])
+	  AC_MSG_RESULT(yes) ], AC_MSG_RESULT(no))
+
+needsignal=no
+haveselect=no
+AC_CHECK_FUNCS(select, [haveselect=yes], )
+if test "x$haveselect" = "xyes" ; then
+	AC_LIBOBJ(select)
+	needsignal=yes
+fi
+
+havepoll=no
+AC_CHECK_FUNCS(poll, [havepoll=yes], )
+if test "x$havepoll" = "xyes" ; then
+	AC_LIBOBJ(poll)
+	needsignal=yes
+fi
+
+haveepoll=no
+AC_CHECK_FUNCS(epoll_ctl, [haveepoll=yes], )
+if test "x$haveepoll" = "xyes" ; then
+	AC_DEFINE(HAVE_EPOLL, 1,
+		[Define if your system supports the epoll system calls])
+	AC_LIBOBJ(epoll)
+	needsignal=yes
+fi
+
+havedevpoll=no
+if test "x$ac_cv_header_sys_devpoll_h" = "xyes"; then
+	AC_DEFINE(HAVE_DEVPOLL, 1,
+		    [Define if /dev/poll is available])
+        AC_LIBOBJ(devpoll)
+fi
+
+havekqueue=no
+if test "x$ac_cv_header_sys_event_h" = "xyes"; then
+	AC_CHECK_FUNCS(kqueue, [havekqueue=yes], )
+	if test "x$havekqueue" = "xyes" ; then
+		AC_MSG_CHECKING(for working kqueue)
+		AC_TRY_RUN(
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main(int argc, char **argv)
+{
+	int kq;
+	int n;
+	int fd[[2]];
+	struct kevent ev;
+	struct timespec ts;
+	char buf[[8000]];
+
+	if (pipe(fd) == -1)
+		exit(1);
+	if (fcntl(fd[[1]], F_SETFL, O_NONBLOCK) == -1)
+		exit(1);
+
+	while ((n = write(fd[[1]], buf, sizeof(buf))) == sizeof(buf))
+		;
+
+        if ((kq = kqueue()) == -1)
+		exit(1);
+
+	ev.ident = fd[[1]];
+	ev.filter = EVFILT_WRITE;
+	ev.flags = EV_ADD | EV_ENABLE;
+	n = kevent(kq, &ev, 1, NULL, 0, NULL);
+	if (n == -1)
+		exit(1);
+	
+	read(fd[[0]], buf, sizeof(buf));
+
+	ts.tv_sec = 0;
+	ts.tv_nsec = 0;
+	n = kevent(kq, NULL, 0, &ev, 1, &ts);
+	if (n == -1 || n == 0)
+		exit(1);
+
+	exit(0);
+}, [AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_WORKING_KQUEUE, 1,
+		[Define if kqueue works correctly with pipes])
+    AC_LIBOBJ(kqueue)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+	fi
+fi
+
+haveepollsyscall=no
+if test "x$ac_cv_header_sys_epoll_h" = "xyes"; then
+	if test "x$haveepoll" = "xno" ; then
+		AC_MSG_CHECKING(for epoll system call)
+		AC_TRY_RUN(
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+int
+epoll_create(int size)
+{
+	return (syscall(__NR_epoll_create, size));
+}
+
+int
+main(int argc, char **argv)
+{
+	int epfd;
+
+	epfd = epoll_create(256);
+	exit (epfd == -1 ? 1 : 0);
+}, [AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_EPOLL, 1,
+	[Define if your system supports the epoll system calls])
+    needsignal=yes
+    AC_LIBOBJ(epoll_sub)
+    AC_LIBOBJ(epoll)], AC_MSG_RESULT(no), AC_MSG_RESULT(no))
+	fi
+fi
+
+haveeventports=no
+AC_CHECK_FUNCS(port_create, [haveeventports=yes], )
+if test "x$haveeventports" = "xyes" ; then
+	AC_DEFINE(HAVE_EVENT_PORTS, 1,
+		[Define if your system supports event ports])
+	AC_LIBOBJ(evport)
+	needsignal=yes
+fi
+if test "x$bwin32" = "xtrue"; then
+	needsignal=yes
+fi
+if test "x$bwin32" = "xtrue"; then
+	needsignal=yes
+fi
+if test "x$needsignal" = "xyes" ; then
+	AC_LIBOBJ(signal)
+fi
+
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_CHECK_TYPES([uint64_t, uint32_t, uint16_t, uint8_t], , ,
+[#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif])
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(long)   
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(short)
+AC_CHECK_TYPES([struct in6_addr], , ,
+[#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif])
+
+AC_MSG_CHECKING([for socklen_t])
+AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>],
+  [socklen_t x;],
+  AC_MSG_RESULT([yes]),
+  [AC_MSG_RESULT([no])
+  AC_DEFINE(socklen_t, unsigned int,
+	[Define to unsigned int if you dont have it])]
+)
+
+AC_MSG_CHECKING([whether our compiler supports __func__])
+AC_TRY_COMPILE([],
+ [ const char *cp = __func__; ],
+ AC_MSG_RESULT([yes]),
+ AC_MSG_RESULT([no])
+ AC_MSG_CHECKING([whether our compiler supports __FUNCTION__])
+ AC_TRY_COMPILE([],
+   [ const char *cp = __FUNCTION__; ],
+   AC_MSG_RESULT([yes])
+   AC_DEFINE(__func__, __FUNCTION__,
+         [Define to appropriate substitue if compiler doesnt have __func__]),
+   AC_MSG_RESULT([no])
+   AC_DEFINE(__func__, __FILE__,
+         [Define to appropriate substitue if compiler doesnt have __func__])))

=== modified file 'extra/libevent/Makefile.am'
--- a/extra/libevent/Makefile.am	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/Makefile.am	2008-04-29 03:50:47 +0000
@@ -1,15 +1,37 @@
 AUTOMAKE_OPTIONS = foreign no-dependencies
 
-EXTRA_DIST = README compat/sys
+EXTRA_DIST = README compat/sys						      \
+	buffer.c epoll.c evbuffer.c event.c evport.c evutil.c kqueue.c poll.c \
+	signal.c devpoll.c epoll_sub.c evdns.c event_tagging.c evrpc.c http.c \
+	log.c select.c strlcpy.c					      \
+	evdns.h event.h evrpc-internal.h evsignal.h http-internal.h log.h     \
+	min_heap.h event-internal.h evhttp.h evrpc.h evutil.h strlcpy-internal.h
+
+
+DISTCLEANFILES = event-config.h
 
 noinst_LIBRARIES = libevent.a
 
-libevent_a_SOURCES = event.c buffer.c evbuffer.c log.c event_tagging.c \
-	http.c evhttp.h http-internal.h evdns.c evdns.h strlcpy.c \
-	strlcpy-internal.h strlcpy-internal.h event-internal.h \
+libevent_a_SOURCES = event.c buffer.c evbuffer.c log.c evutil.c \
         select.c poll.c epoll.c epoll_sub.c devpoll.c kqueue.c \
-	evport.c evsignal.h signal.c
-
-include_HEADERS = event.h evhttp.h evdns.h log.h
+	evport.c signal.c
+
+include_HEADERS = event.h evutil.h event-config.h
+
+BUILT_SOURCES = event-config.h
+
+event-config.h: $(top_srcdir)/include/config.h
+	echo '/* event-config.h' > $@
+	echo ' * Generated by autoconf; post-processed by libevent.' >> $@
+	echo ' * Do not edit this file.' >> $@
+	echo ' * Do not rely on macros in this file existing in later versions.'>> $@
+	echo ' */' >> $@
+	echo '#ifndef _EVENT_CONFIG_H_' >> $@
+	echo '#define _EVENT_CONFIG_H_' >> $@
+
+	sed -e 's/#define /#define _EVENT_/' \
+	    -e 's/#undef /#undef _EVENT_/' \
+	    -e 's/#ifndef /#ifndef _EVENT_/' < $(top_srcdir)/include/config.h >> $@
+	echo "#endif" >> $@
 
 INCLUDES = -Icompat -I$(top_srcdir)/include

=== modified file 'extra/libevent/README'
--- a/extra/libevent/README	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/README	2008-04-28 17:52:20 +0000
@@ -2,6 +2,10 @@
 
 $ ./configure && make
 
+     (If you got libevent from the subversion repository, you will
+      first need to run the included "autogen.sh" script in order to
+      generate the configure script.)
+
 Install as root via
 
 # make install
@@ -15,7 +19,7 @@
 To enable the low-level tracing build the library as:
 
 CFLAGS=-DUSE_DEBUG ./configure [...]
- 
+
 Acknowledgements:
 -----------------
 

=== modified file 'extra/libevent/buffer.c'
--- a/extra/libevent/buffer.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/buffer.c	2008-04-28 17:52:20 +0000
@@ -29,6 +29,11 @@
 #include "config.h"
 #endif
 
+#ifdef WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
 #ifdef HAVE_VASPRINTF
 /* If we have vasprintf, we need to define this before we include stdio.h. */
 #define _GNU_SOURCE
@@ -57,6 +62,7 @@
 #endif
 
 #include "event.h"
+#include "config.h"
 
 struct evbuffer *
 evbuffer_new(void)
@@ -351,12 +357,14 @@
 	u_char *p;
 	size_t oldoff = buf->off;
 	int n = EVBUFFER_MAX_READ;
+
+#if defined(FIONREAD)
 #ifdef WIN32
-	DWORD dwBytesRead;
-#endif
-
-#ifdef FIONREAD
+	long lng = n;
+	if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) {
+#else
 	if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
+#endif
 		n = EVBUFFER_MAX_READ;
 	} else if (n > EVBUFFER_MAX_READ && n > howmuch) {
 		/*
@@ -384,18 +392,13 @@
 
 #ifndef WIN32
 	n = read(fd, p, howmuch);
-	if (n == -1)
-		return (-1);
-	if (n == 0)
-		return (0);
 #else
-	n = ReadFile((HANDLE)fd, p, howmuch, &dwBytesRead, NULL);
-	if (n == 0)
-		return (-1);
-	if (dwBytesRead == 0)
-		return (0);
-	n = dwBytesRead;
+	n = recv(fd, p, howmuch, 0);
 #endif
+	if (n == -1)
+		return (-1);
+	if (n == 0)
+		return (0);
 
 	buf->off += n;
 
@@ -410,24 +413,16 @@
 evbuffer_write(struct evbuffer *buffer, int fd)
 {
 	int n;
-#ifdef WIN32
-	DWORD dwBytesWritten;
-#endif
 
 #ifndef WIN32
 	n = write(fd, buffer->buffer, buffer->off);
-	if (n == -1)
-		return (-1);
-	if (n == 0)
-		return (0);
 #else
-	n = WriteFile((HANDLE)fd, buffer->buffer, buffer->off, &dwBytesWritten, NULL);
-	if (n == 0)
-		return (-1);
-	if (dwBytesWritten == 0)
-		return (0);
-	n = dwBytesWritten;
+	n = send(fd, buffer->buffer, buffer->off, 0);
 #endif
+	if (n == -1)
+		return (-1);
+	if (n == 0)
+		return (0);
 	evbuffer_drain(buffer, n);
 
 	return (n);

=== removed file 'extra/libevent/compat/sys/tree.h'
--- a/extra/libevent/compat/sys/tree.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/compat/sys/tree.h	1970-01-01 00:00:00 +0000
@@ -1,677 +0,0 @@
-/*	$OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $	*/
-/*
- * Copyright 2002 Niels Provos <provos@stripped>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef	_SYS_TREE_H_
-#define	_SYS_TREE_H_
-
-/*
- * This file defines data structures for different types of trees:
- * splay trees and red-black trees.
- *
- * A splay tree is a self-organizing data structure.  Every operation
- * on the tree causes a splay to happen.  The splay moves the requested
- * node to the root of the tree and partly rebalances it.
- *
- * This has the benefit that request locality causes faster lookups as
- * the requested nodes move to the top of the tree.  On the other hand,
- * every lookup causes memory writes.
- *
- * The Balance Theorem bounds the total access time for m operations
- * and n inserts on an initially empty tree as O((m + n)lg n).  The
- * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
- *
- * A red-black tree is a binary search tree with the node color as an
- * extra attribute.  It fulfills a set of conditions:
- *	- every search path from the root to a leaf consists of the
- *	  same number of black nodes,
- *	- each red node (except for the root) has a black parent,
- *	- each leaf node is black.
- *
- * Every operation on a red-black tree is bounded as O(lg n).
- * The maximum height of a red-black tree is 2lg (n+1).
- */
-
-#define SPLAY_HEAD(name, type)						\
-struct name {								\
-	struct type *sph_root; /* root of the tree */			\
-}
-
-#define SPLAY_INITIALIZER(root)						\
-	{ NULL }
-
-#define SPLAY_INIT(root) do {						\
-	(root)->sph_root = NULL;					\
-} while (0)
-
-#define SPLAY_ENTRY(type)						\
-struct {								\
-	struct type *spe_left; /* left element */			\
-	struct type *spe_right; /* right element */			\
-}
-
-#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
-#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
-#define SPLAY_ROOT(head)		(head)->sph_root
-#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
-
-/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
-#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
-	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
-	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
-	(head)->sph_root = tmp;						\
-} while (0)
-	
-#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
-	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
-	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
-	(head)->sph_root = tmp;						\
-} while (0)
-
-#define SPLAY_LINKLEFT(head, tmp, field) do {				\
-	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
-	tmp = (head)->sph_root;						\
-	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
-} while (0)
-
-#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
-	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
-	tmp = (head)->sph_root;						\
-	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
-} while (0)
-
-#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
-	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
-	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
-	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
-	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
-} while (0)
-
-/* Generates prototypes and inline functions */
-
-#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
-void name##_SPLAY(struct name *, struct type *);			\
-void name##_SPLAY_MINMAX(struct name *, int);				\
-struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
-struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
-									\
-/* Finds the node with the same key as elm */				\
-static __inline struct type *						\
-name##_SPLAY_FIND(struct name *head, struct type *elm)			\
-{									\
-	if (SPLAY_EMPTY(head))						\
-		return(NULL);						\
-	name##_SPLAY(head, elm);					\
-	if ((cmp)(elm, (head)->sph_root) == 0)				\
-		return (head->sph_root);				\
-	return (NULL);							\
-}									\
-									\
-static __inline struct type *						\
-name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
-{									\
-	name##_SPLAY(head, elm);					\
-	if (SPLAY_RIGHT(elm, field) != NULL) {				\
-		elm = SPLAY_RIGHT(elm, field);				\
-		while (SPLAY_LEFT(elm, field) != NULL) {		\
-			elm = SPLAY_LEFT(elm, field);			\
-		}							\
-	} else								\
-		elm = NULL;						\
-	return (elm);							\
-}									\
-									\
-static __inline struct type *						\
-name##_SPLAY_MIN_MAX(struct name *head, int val)			\
-{									\
-	name##_SPLAY_MINMAX(head, val);					\
-        return (SPLAY_ROOT(head));					\
-}
-
-/* Main splay operation.
- * Moves node close to the key of elm to top
- */
-#define SPLAY_GENERATE(name, type, field, cmp)				\
-struct type *								\
-name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
-{									\
-    if (SPLAY_EMPTY(head)) {						\
-	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
-    } else {								\
-	    int __comp;							\
-	    name##_SPLAY(head, elm);					\
-	    __comp = (cmp)(elm, (head)->sph_root);			\
-	    if(__comp < 0) {						\
-		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
-		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
-		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
-	    } else if (__comp > 0) {					\
-		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
-		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
-		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
-	    } else							\
-		    return ((head)->sph_root);				\
-    }									\
-    (head)->sph_root = (elm);						\
-    return (NULL);							\
-}									\
-									\
-struct type *								\
-name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
-{									\
-	struct type *__tmp;						\
-	if (SPLAY_EMPTY(head))						\
-		return (NULL);						\
-	name##_SPLAY(head, elm);					\
-	if ((cmp)(elm, (head)->sph_root) == 0) {			\
-		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
-			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
-		} else {						\
-			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
-			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
-			name##_SPLAY(head, elm);			\
-			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
-		}							\
-		return (elm);						\
-	}								\
-	return (NULL);							\
-}									\
-									\
-void									\
-name##_SPLAY(struct name *head, struct type *elm)			\
-{									\
-	struct type __node, *__left, *__right, *__tmp;			\
-	int __comp;							\
-\
-	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-	__left = __right = &__node;					\
-\
-	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
-		if (__comp < 0) {					\
-			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if ((cmp)(elm, __tmp) < 0){			\
-				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
-				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			SPLAY_LINKLEFT(head, __right, field);		\
-		} else if (__comp > 0) {				\
-			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if ((cmp)(elm, __tmp) > 0){			\
-				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
-				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			SPLAY_LINKRIGHT(head, __left, field);		\
-		}							\
-	}								\
-	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
-}									\
-									\
-/* Splay with either the minimum or the maximum element			\
- * Used to find minimum or maximum element in tree.			\
- */									\
-void name##_SPLAY_MINMAX(struct name *head, int __comp) \
-{									\
-	struct type __node, *__left, *__right, *__tmp;			\
-\
-	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
-	__left = __right = &__node;					\
-\
-	while (1) {							\
-		if (__comp < 0) {					\
-			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if (__comp < 0){				\
-				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
-				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			SPLAY_LINKLEFT(head, __right, field);		\
-		} else if (__comp > 0) {				\
-			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
-			if (__tmp == NULL)				\
-				break;					\
-			if (__comp > 0) {				\
-				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
-				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
-					break;				\
-			}						\
-			SPLAY_LINKRIGHT(head, __left, field);		\
-		}							\
-	}								\
-	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
-}
-
-#define SPLAY_NEGINF	-1
-#define SPLAY_INF	1
-
-#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
-#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
-#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
-#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
-#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
-					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
-#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
-					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
-
-#define SPLAY_FOREACH(x, name, head)					\
-	for ((x) = SPLAY_MIN(name, head);				\
-	     (x) != NULL;						\
-	     (x) = SPLAY_NEXT(name, head, x))
-
-/* Macros that define a red-back tree */
-#define RB_HEAD(name, type)						\
-struct name {								\
-	struct type *rbh_root; /* root of the tree */			\
-}
-
-#define RB_INITIALIZER(root)						\
-	{ NULL }
-
-#define RB_INIT(root) do {						\
-	(root)->rbh_root = NULL;					\
-} while (0)
-
-#define RB_BLACK	0
-#define RB_RED		1
-#define RB_ENTRY(type)							\
-struct {								\
-	struct type *rbe_left;		/* left element */		\
-	struct type *rbe_right;		/* right element */		\
-	struct type *rbe_parent;	/* parent element */		\
-	int rbe_color;			/* node color */		\
-}
-
-#define RB_LEFT(elm, field)		(elm)->field.rbe_left
-#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
-#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
-#define RB_COLOR(elm, field)		(elm)->field.rbe_color
-#define RB_ROOT(head)			(head)->rbh_root
-#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
-
-#define RB_SET(elm, parent, field) do {					\
-	RB_PARENT(elm, field) = parent;					\
-	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
-	RB_COLOR(elm, field) = RB_RED;					\
-} while (0)
-
-#define RB_SET_BLACKRED(black, red, field) do {				\
-	RB_COLOR(black, field) = RB_BLACK;				\
-	RB_COLOR(red, field) = RB_RED;					\
-} while (0)
-
-#ifndef RB_AUGMENT
-#define RB_AUGMENT(x)
-#endif
-
-#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
-	(tmp) = RB_RIGHT(elm, field);					\
-	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
-		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
-	}								\
-	RB_AUGMENT(elm);						\
-	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
-		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
-			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
-		else							\
-			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
-	} else								\
-		(head)->rbh_root = (tmp);				\
-	RB_LEFT(tmp, field) = (elm);					\
-	RB_PARENT(elm, field) = (tmp);					\
-	RB_AUGMENT(tmp);						\
-	if ((RB_PARENT(tmp, field)))					\
-		RB_AUGMENT(RB_PARENT(tmp, field));			\
-} while (0)
-
-#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
-	(tmp) = RB_LEFT(elm, field);					\
-	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
-		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
-	}								\
-	RB_AUGMENT(elm);						\
-	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
-		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
-			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
-		else							\
-			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
-	} else								\
-		(head)->rbh_root = (tmp);				\
-	RB_RIGHT(tmp, field) = (elm);					\
-	RB_PARENT(elm, field) = (tmp);					\
-	RB_AUGMENT(tmp);						\
-	if ((RB_PARENT(tmp, field)))					\
-		RB_AUGMENT(RB_PARENT(tmp, field));			\
-} while (0)
-
-/* Generates prototypes and inline functions */
-#define RB_PROTOTYPE(name, type, field, cmp)				\
-void name##_RB_INSERT_COLOR(struct name *, struct type *);	\
-void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
-struct type *name##_RB_REMOVE(struct name *, struct type *);		\
-struct type *name##_RB_INSERT(struct name *, struct type *);		\
-struct type *name##_RB_FIND(struct name *, struct type *);		\
-struct type *name##_RB_NEXT(struct type *);				\
-struct type *name##_RB_MINMAX(struct name *, int);			\
-									\
-
-/* Main rb operation.
- * Moves node close to the key of elm to top
- */
-#define RB_GENERATE(name, type, field, cmp)				\
-void									\
-name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
-{									\
-	struct type *parent, *gparent, *tmp;				\
-	while ((parent = RB_PARENT(elm, field)) &&			\
-	    RB_COLOR(parent, field) == RB_RED) {			\
-		gparent = RB_PARENT(parent, field);			\
-		if (parent == RB_LEFT(gparent, field)) {		\
-			tmp = RB_RIGHT(gparent, field);			\
-			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
-				RB_COLOR(tmp, field) = RB_BLACK;	\
-				RB_SET_BLACKRED(parent, gparent, field);\
-				elm = gparent;				\
-				continue;				\
-			}						\
-			if (RB_RIGHT(parent, field) == elm) {		\
-				RB_ROTATE_LEFT(head, parent, tmp, field);\
-				tmp = parent;				\
-				parent = elm;				\
-				elm = tmp;				\
-			}						\
-			RB_SET_BLACKRED(parent, gparent, field);	\
-			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
-		} else {						\
-			tmp = RB_LEFT(gparent, field);			\
-			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
-				RB_COLOR(tmp, field) = RB_BLACK;	\
-				RB_SET_BLACKRED(parent, gparent, field);\
-				elm = gparent;				\
-				continue;				\
-			}						\
-			if (RB_LEFT(parent, field) == elm) {		\
-				RB_ROTATE_RIGHT(head, parent, tmp, field);\
-				tmp = parent;				\
-				parent = elm;				\
-				elm = tmp;				\
-			}						\
-			RB_SET_BLACKRED(parent, gparent, field);	\
-			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
-		}							\
-	}								\
-	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
-}									\
-									\
-void									\
-name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
-{									\
-	struct type *tmp;						\
-	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
-	    elm != RB_ROOT(head)) {					\
-		if (RB_LEFT(parent, field) == elm) {			\
-			tmp = RB_RIGHT(parent, field);			\
-			if (RB_COLOR(tmp, field) == RB_RED) {		\
-				RB_SET_BLACKRED(tmp, parent, field);	\
-				RB_ROTATE_LEFT(head, parent, tmp, field);\
-				tmp = RB_RIGHT(parent, field);		\
-			}						\
-			if ((RB_LEFT(tmp, field) == NULL ||		\
-			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-			    (RB_RIGHT(tmp, field) == NULL ||		\
-			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-				RB_COLOR(tmp, field) = RB_RED;		\
-				elm = parent;				\
-				parent = RB_PARENT(elm, field);		\
-			} else {					\
-				if (RB_RIGHT(tmp, field) == NULL ||	\
-				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
-					struct type *oleft;		\
-					if ((oleft = RB_LEFT(tmp, field)))\
-						RB_COLOR(oleft, field) = RB_BLACK;\
-					RB_COLOR(tmp, field) = RB_RED;	\
-					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
-					tmp = RB_RIGHT(parent, field);	\
-				}					\
-				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-				RB_COLOR(parent, field) = RB_BLACK;	\
-				if (RB_RIGHT(tmp, field))		\
-					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
-				RB_ROTATE_LEFT(head, parent, tmp, field);\
-				elm = RB_ROOT(head);			\
-				break;					\
-			}						\
-		} else {						\
-			tmp = RB_LEFT(parent, field);			\
-			if (RB_COLOR(tmp, field) == RB_RED) {		\
-				RB_SET_BLACKRED(tmp, parent, field);	\
-				RB_ROTATE_RIGHT(head, parent, tmp, field);\
-				tmp = RB_LEFT(parent, field);		\
-			}						\
-			if ((RB_LEFT(tmp, field) == NULL ||		\
-			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
-			    (RB_RIGHT(tmp, field) == NULL ||		\
-			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
-				RB_COLOR(tmp, field) = RB_RED;		\
-				elm = parent;				\
-				parent = RB_PARENT(elm, field);		\
-			} else {					\
-				if (RB_LEFT(tmp, field) == NULL ||	\
-				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
-					struct type *oright;		\
-					if ((oright = RB_RIGHT(tmp, field)))\
-						RB_COLOR(oright, field) = RB_BLACK;\
-					RB_COLOR(tmp, field) = RB_RED;	\
-					RB_ROTATE_LEFT(head, tmp, oright, field);\
-					tmp = RB_LEFT(parent, field);	\
-				}					\
-				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
-				RB_COLOR(parent, field) = RB_BLACK;	\
-				if (RB_LEFT(tmp, field))		\
-					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
-				RB_ROTATE_RIGHT(head, parent, tmp, field);\
-				elm = RB_ROOT(head);			\
-				break;					\
-			}						\
-		}							\
-	}								\
-	if (elm)							\
-		RB_COLOR(elm, field) = RB_BLACK;			\
-}									\
-									\
-struct type *								\
-name##_RB_REMOVE(struct name *head, struct type *elm)			\
-{									\
-	struct type *child, *parent, *old = elm;			\
-	int color;							\
-	if (RB_LEFT(elm, field) == NULL)				\
-		child = RB_RIGHT(elm, field);				\
-	else if (RB_RIGHT(elm, field) == NULL)				\
-		child = RB_LEFT(elm, field);				\
-	else {								\
-		struct type *left;					\
-		elm = RB_RIGHT(elm, field);				\
-		while ((left = RB_LEFT(elm, field)))			\
-			elm = left;					\
-		child = RB_RIGHT(elm, field);				\
-		parent = RB_PARENT(elm, field);				\
-		color = RB_COLOR(elm, field);				\
-		if (child)						\
-			RB_PARENT(child, field) = parent;		\
-		if (parent) {						\
-			if (RB_LEFT(parent, field) == elm)		\
-				RB_LEFT(parent, field) = child;		\
-			else						\
-				RB_RIGHT(parent, field) = child;	\
-			RB_AUGMENT(parent);				\
-		} else							\
-			RB_ROOT(head) = child;				\
-		if (RB_PARENT(elm, field) == old)			\
-			parent = elm;					\
-		(elm)->field = (old)->field;				\
-		if (RB_PARENT(old, field)) {				\
-			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
-				RB_LEFT(RB_PARENT(old, field), field) = elm;\
-			else						\
-				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
-			RB_AUGMENT(RB_PARENT(old, field));		\
-		} else							\
-			RB_ROOT(head) = elm;				\
-		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
-		if (RB_RIGHT(old, field))				\
-			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
-		if (parent) {						\
-			left = parent;					\
-			do {						\
-				RB_AUGMENT(left);			\
-			} while ((left = RB_PARENT(left, field)));	\
-		}							\
-		goto color;						\
-	}								\
-	parent = RB_PARENT(elm, field);					\
-	color = RB_COLOR(elm, field);					\
-	if (child)							\
-		RB_PARENT(child, field) = parent;			\
-	if (parent) {							\
-		if (RB_LEFT(parent, field) == elm)			\
-			RB_LEFT(parent, field) = child;			\
-		else							\
-			RB_RIGHT(parent, field) = child;		\
-		RB_AUGMENT(parent);					\
-	} else								\
-		RB_ROOT(head) = child;					\
-color:									\
-	if (color == RB_BLACK)						\
-		name##_RB_REMOVE_COLOR(head, parent, child);		\
-	return (old);							\
-}									\
-									\
-/* Inserts a node into the RB tree */					\
-struct type *								\
-name##_RB_INSERT(struct name *head, struct type *elm)			\
-{									\
-	struct type *tmp;						\
-	struct type *parent = NULL;					\
-	int comp = 0;							\
-	tmp = RB_ROOT(head);						\
-	while (tmp) {							\
-		parent = tmp;						\
-		comp = (cmp)(elm, parent);				\
-		if (comp < 0)						\
-			tmp = RB_LEFT(tmp, field);			\
-		else if (comp > 0)					\
-			tmp = RB_RIGHT(tmp, field);			\
-		else							\
-			return (tmp);					\
-	}								\
-	RB_SET(elm, parent, field);					\
-	if (parent != NULL) {						\
-		if (comp < 0)						\
-			RB_LEFT(parent, field) = elm;			\
-		else							\
-			RB_RIGHT(parent, field) = elm;			\
-		RB_AUGMENT(parent);					\
-	} else								\
-		RB_ROOT(head) = elm;					\
-	name##_RB_INSERT_COLOR(head, elm);				\
-	return (NULL);							\
-}									\
-									\
-/* Finds the node with the same key as elm */				\
-struct type *								\
-name##_RB_FIND(struct name *head, struct type *elm)			\
-{									\
-	struct type *tmp = RB_ROOT(head);				\
-	int comp;							\
-	while (tmp) {							\
-		comp = cmp(elm, tmp);					\
-		if (comp < 0)						\
-			tmp = RB_LEFT(tmp, field);			\
-		else if (comp > 0)					\
-			tmp = RB_RIGHT(tmp, field);			\
-		else							\
-			return (tmp);					\
-	}								\
-	return (NULL);							\
-}									\
-									\
-struct type *								\
-name##_RB_NEXT(struct type *elm)					\
-{									\
-	if (RB_RIGHT(elm, field)) {					\
-		elm = RB_RIGHT(elm, field);				\
-		while (RB_LEFT(elm, field))				\
-			elm = RB_LEFT(elm, field);			\
-	} else {							\
-		if (RB_PARENT(elm, field) &&				\
-		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
-			elm = RB_PARENT(elm, field);			\
-		else {							\
-			while (RB_PARENT(elm, field) &&			\
-			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
-				elm = RB_PARENT(elm, field);		\
-			elm = RB_PARENT(elm, field);			\
-		}							\
-	}								\
-	return (elm);							\
-}									\
-									\
-struct type *								\
-name##_RB_MINMAX(struct name *head, int val)				\
-{									\
-	struct type *tmp = RB_ROOT(head);				\
-	struct type *parent = NULL;					\
-	while (tmp) {							\
-		parent = tmp;						\
-		if (val < 0)						\
-			tmp = RB_LEFT(tmp, field);			\
-		else							\
-			tmp = RB_RIGHT(tmp, field);			\
-	}								\
-	return (parent);						\
-}
-
-#define RB_NEGINF	-1
-#define RB_INF	1
-
-#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
-#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
-#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
-#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
-#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
-#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
-
-#define RB_FOREACH(x, name, head)					\
-	for ((x) = RB_MIN(name, head);					\
-	     (x) != NULL;						\
-	     (x) = name##_RB_NEXT(x))
-
-#endif	/* _SYS_TREE_H_ */

=== modified file 'extra/libevent/devpoll.c'
--- a/extra/libevent/devpoll.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/devpoll.c	2008-04-28 17:52:20 +0000
@@ -38,7 +38,6 @@
 #include <sys/_time.h>
 #endif
 #include <sys/queue.h>
-#include <sys/tree.h>
 #include <sys/devpoll.h>
 #include <signal.h>
 #include <stdio.h>
@@ -72,21 +71,20 @@
 	int nchanges;
 };
 
-void *devpoll_init	(struct event_base *);
-int devpoll_add	(void *, struct event *);
-int devpoll_del	(void *, struct event *);
-int devpoll_recalc	(struct event_base *, void *, int);
-int devpoll_dispatch	(struct event_base *, void *, struct timeval *);
-void devpoll_dealloc	(struct event_base *, void *);
+static void *devpoll_init	(struct event_base *);
+static int devpoll_add	(void *, struct event *);
+static int devpoll_del	(void *, struct event *);
+static int devpoll_dispatch	(struct event_base *, void *, struct timeval *);
+static void devpoll_dealloc	(struct event_base *, void *);
 
 struct eventop devpollops = {
 	"devpoll",
 	devpoll_init,
 	devpoll_add,
 	devpoll_del,
-	devpoll_recalc,
 	devpoll_dispatch,
-	devpoll_dealloc
+	devpoll_dealloc,
+	1 /* need reinit */
 };
 
 #define NEVENT	32000
@@ -127,7 +125,7 @@
 	return(0);
 }
 
-void *
+static void *
 devpoll_init(struct event_base *base)
 {
 	int dpfd, nfiles = NEVENT;
@@ -186,7 +184,7 @@
 	return (devpollop);
 }
 
-int
+static int
 devpoll_recalc(struct event_base *base, void *arg, int max)
 {
 	struct devpollop *devpollop = arg;
@@ -213,7 +211,7 @@
 	return (0);
 }
 
-int
+static int
 devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 {
 	struct devpollop *devpollop = arg;
@@ -290,7 +288,7 @@
 }
 
 
-int
+static int
 devpoll_add(void *arg, struct event *ev)
 {
 	struct devpollop *devpollop = arg;
@@ -344,7 +342,7 @@
 	return (0);
 }
 
-int
+static int
 devpoll_del(void *arg, struct event *ev)
 {
 	struct devpollop *devpollop = arg;
@@ -401,7 +399,7 @@
 	return (0);
 }
 
-void
+static void
 devpoll_dealloc(struct event_base *base, void *arg)
 {
 	struct devpollop *devpollop = arg;

=== modified file 'extra/libevent/epoll.c'
--- a/extra/libevent/epoll.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/epoll.c	2008-04-28 17:52:20 +0000
@@ -32,7 +32,6 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <sys/tree.h>
 #include <sys/resource.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -72,21 +71,20 @@
 	int epfd;
 };
 
-void *epoll_init	(struct event_base *);
-int epoll_add	(void *, struct event *);
-int epoll_del	(void *, struct event *);
-int epoll_recalc	(struct event_base *, void *, int);
-int epoll_dispatch	(struct event_base *, void *, struct timeval *);
-void epoll_dealloc	(struct event_base *, void *);
+static void *epoll_init	(struct event_base *);
+static int epoll_add	(void *, struct event *);
+static int epoll_del	(void *, struct event *);
+static int epoll_dispatch	(struct event_base *, void *, struct timeval *);
+static void epoll_dealloc	(struct event_base *, void *);
 
 struct eventop epollops = {
 	"epoll",
 	epoll_init,
 	epoll_add,
 	epoll_del,
-	epoll_recalc,
 	epoll_dispatch,
-	epoll_dealloc
+	epoll_dealloc,
+	1 /* need reinit */
 };
 
 #ifdef HAVE_SETFD
@@ -100,7 +98,7 @@
 
 #define NEVENT	32000
 
-void *
+static void *
 epoll_init(struct event_base *base)
 {
 	int epfd, nfiles = NEVENT;
@@ -156,7 +154,7 @@
 	return (epollop);
 }
 
-int
+static int
 epoll_recalc(struct event_base *base, void *arg, int max)
 {
 	struct epollop *epollop = arg;
@@ -183,7 +181,7 @@
 	return (0);
 }
 
-int
+static int
 epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 {
 	struct epollop *epollop = arg;
@@ -211,36 +209,27 @@
 	event_debug(("%s: epoll_wait reports %d", __func__, res));
 
 	for (i = 0; i < res; i++) {
-		int which = 0;
 		int what = events[i].events;
 		struct event *evread = NULL, *evwrite = NULL;
 
 		evep = (struct evepoll *)events[i].data.ptr;
-   
-                if (what & EPOLLHUP)
-                        what |= EPOLLIN | EPOLLOUT;
-                else if (what & EPOLLERR)
-                        what |= EPOLLIN | EPOLLOUT;
 
-		if (what & EPOLLIN) {
+		if (what & (EPOLLHUP|EPOLLERR)) {
 			evread = evep->evread;
-			which |= EV_READ;
-		}
-
-		if (what & EPOLLOUT) {
 			evwrite = evep->evwrite;
-			which |= EV_WRITE;
+		} else {
+			if (what & EPOLLIN) {
+				evread = evep->evread;
+			}
+
+			if (what & EPOLLOUT) {
+				evwrite = evep->evwrite;
+			}
 		}
 
-		if (!which)
+		if (!(evread||evwrite))
 			continue;
 
-		if (evread != NULL && !(evread->ev_events & EV_PERSIST))
-			event_del(evread);
-		if (evwrite != NULL && evwrite != evread &&
-		    !(evwrite->ev_events & EV_PERSIST))
-			event_del(evwrite);
-
 		if (evread != NULL)
 			event_active(evread, EV_READ, 1);
 		if (evwrite != NULL)
@@ -251,7 +240,7 @@
 }
 
 
-int
+static int
 epoll_add(void *arg, struct event *ev)
 {
 	struct epollop *epollop = arg;
@@ -299,7 +288,7 @@
 	return (0);
 }
 
-int
+static int
 epoll_del(void *arg, struct event *ev)
 {
 	struct epollop *epollop = arg;
@@ -350,7 +339,7 @@
 	return (0);
 }
 
-void
+static void
 epoll_dealloc(struct event_base *base, void *arg)
 {
 	struct epollop *epollop = arg;

=== modified file 'extra/libevent/evbuffer.c'
--- a/extra/libevent/evbuffer.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/evbuffer.c	2008-04-28 17:52:20 +0000
@@ -43,6 +43,11 @@
 #include <stdarg.h>
 #endif
 
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+#include "evutil.h"
 #include "event.h"
 
 /* prototypes */
@@ -56,7 +61,7 @@
 	struct timeval tv, *ptv = NULL;
 
 	if (timeout) {
-		timerclear(&tv);
+		evutil_timerclear(&tv);
 		tv.tv_sec = timeout;
 		ptv = &tv;
 	}
@@ -288,7 +293,7 @@
  */
 
 int
-bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
+bufferevent_write(struct bufferevent *bufev, const void *data, size_t size)
 {
 	int res;
 

=== modified file 'extra/libevent/evdns.c'
--- a/extra/libevent/evdns.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/evdns.c	2008-04-28 17:52:20 +0000
@@ -43,16 +43,20 @@
 #include "misc.h"
 #endif
 
-/* #define NDEBUG */
+#ifdef DNS_USE_FTIME_FOR_ID
+#include <sys/timeb.h>
+#endif
 
 #ifndef DNS_USE_CPU_CLOCK_FOR_ID
 #ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
 #ifndef DNS_USE_OPENSSL_FOR_ID
+#ifndef DNS_USE_FTIME_FOR_ID
 #error Must configure at least one id generation method.
 #error Please see the documentation.
 #endif
 #endif
 #endif
+#endif
 
 /* #define _POSIX_C_SOURCE 200507 */
 #define _GNU_SOURCE
@@ -78,7 +82,9 @@
 
 #include <string.h>
 #include <fcntl.h>
+#ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
+#endif
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif
@@ -86,7 +92,9 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <limits.h>
 #include <sys/stat.h>
 #include <ctype.h>
@@ -94,11 +102,13 @@
 #include <stdarg.h>
 
 #include "evdns.h"
+#include "evutil.h"
 #include "log.h"
 #ifdef WIN32
+#include <winsock2.h>
 #include <windows.h>
-#include <winsock2.h>
 #include <iphlpapi.h>
+#include <io.h>
 #else
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -109,10 +119,6 @@
 #include <netinet/in6.h>
 #endif
 
-#ifdef WIN32
-typedef int socklen_t;
-#endif
-
 #define EVDNS_LOG_DEBUG 0
 #define EVDNS_LOG_WARN 1
 
@@ -120,26 +126,32 @@
 #define HOST_NAME_MAX 255
 #endif
 
-#ifndef NDEBUG
 #include <stdio.h>
-#endif
 
 #undef MIN
 #define MIN(a,b) ((a)<(b)?(a):(b))
 
 #ifdef __USE_ISOC99B
 /* libevent doesn't work without this */
-typedef uint8_t u_char;
+typedef ev_uint8_t u_char;
 typedef unsigned int uint;
 #endif
 #include <event.h>
 
-#define u64 uint64_t
-#define u32 uint32_t
-#define u16 uint16_t
-#define u8  uint8_t
-
-#define MAX_ADDRS 4  /* maximum number of addresses from a single packet */
+#define u64 ev_uint64_t
+#define u32 ev_uint32_t
+#define u16 ev_uint16_t
+#define u8  ev_uint8_t
+
+#ifdef WIN32
+#define snprintf _snprintf
+#define open _open
+#define read _read
+#define close _close
+#define strdup _strdup
+#endif
+
+#define MAX_ADDRS 32  /* maximum number of addresses from a single packet */
 /* which we bother recording */
 
 #define TYPE_A         EVDNS_TYPE_A
@@ -319,7 +331,7 @@
 static void evdns_requests_pump_waiting_queue(void);
 static u16 transaction_id_pick(void);
 static struct request *request_new(int type, const char *name, int flags,
evdns_callback_type callback, void *ptr);
-static void request_submit(struct request *req);
+static void request_submit(struct request *const req);
 
 static int server_request_free(struct server_request *req);
 static void server_request_free_answers(struct server_request *req);
@@ -352,7 +364,7 @@
 static int
 inet_aton(const char *c, struct in_addr *addr)
 {
-	uint32_t r;
+	ev_uint32_t r;
 	if (strcmp(c, "255.255.255.255") == 0) {
 		addr->s_addr = 0xffffffffu;
 	} else {
@@ -363,17 +375,15 @@
 	}
 	return 1;
 }
-#define CLOSE_SOCKET(x) closesocket(x)
 #else
 #define last_error(sock) (errno)
 #define error_is_eagain(err) ((err) == EAGAIN)
-#define CLOSE_SOCKET(x) close(x)
 #endif
+#define CLOSE_SOCKET(s) EVUTIL_CLOSESOCKET(s)
 
 #define ISSPACE(c) isspace((int)(unsigned char)(c))
 #define ISDIGIT(c) isdigit((int)(unsigned char)(c))
 
-#ifndef NDEBUG
 static const char *
 debug_ntoa(u32 address)
 {
@@ -386,7 +396,6 @@
   		      (int)(u8)((a    )&0xff));
 	return buf;
 }
-#endif
 
 static evdns_debug_log_fn_type evdns_log_fn = NULL;
 
@@ -852,7 +861,7 @@
 		 */
 		SKIP_NAME;
 		j += 4;
-		if (j >= length) goto err;
+		if (j > length) goto err;
 	}
 
 	/* now we have the answer section which looks like
@@ -953,8 +962,7 @@
 	GET16(additional);
 
 	if (flags & 0x8000) return -1; /* Must not be an answer. */
-	if (flags & 0x7800) return -1; /* only standard queries are supported */
-	flags &= 0x0300; /* Only TC and RD get preserved. */
+	flags &= 0x0110; /* Only RD and CD get preserved. */
 
 	server_req = malloc(sizeof(struct server_request));
 	if (server_req == NULL) return -1;
@@ -983,7 +991,7 @@
 		if (!q)
 			goto err;
 		q->type = type;
-		q->class = class;
+		q->dns_question_class = class;
 		memcpy(q->name, tmp_name, namelen+1);
 		server_req->base.questions[server_req->base.nquestions++] = q;
 	}
@@ -992,6 +1000,13 @@
 
 	server_req->port = port;
 	port->refcnt++;
+
+	/* Only standard queries are supported. */
+	if (flags & 0x7800) {
+		evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL);
+		return -1;
+	}
+
 	port->user_callback(&(server_req->base), port->user_data);
 
 	return 0;
@@ -1012,41 +1027,65 @@
 #undef GET8
 }
 
+static u16
+default_transaction_id_fn(void)
+{
+	u16 trans_id;
+#ifdef DNS_USE_CPU_CLOCK_FOR_ID
+	struct timespec ts;
+#ifdef CLOCK_MONOTONIC
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
+#else
+	if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
+#endif
+	event_err(1, "clock_gettime");
+	trans_id = ts.tv_nsec & 0xffff;
+#endif
+
+#ifdef DNS_USE_FTIME_FOR_ID
+	struct _timeb tb;
+	_ftime(&tb);
+	trans_id = tb.millitm & 0xffff;
+#endif
+
+#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	trans_id = tv.tv_usec & 0xffff;
+#endif
+
+#ifdef DNS_USE_OPENSSL_FOR_ID
+	if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
+		/* in the case that the RAND call fails we back */
+		/* down to using gettimeofday. */
+		/*
+		  struct timeval tv;
+		  gettimeofday(&tv, NULL);
+		  trans_id = tv.tv_usec & 0xffff;
+		*/
+		abort();
+	}
+#endif
+	return trans_id;
+}
+
+static ev_uint16_t (*trans_id_function)(void) = default_transaction_id_fn;
+
+void
+evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void))
+{
+	if (fn)
+		trans_id_function = fn;
+	else
+		trans_id_function = default_transaction_id_fn;
+}
+
 /* Try to choose a strong transaction id which isn't already in flight */
 static u16
 transaction_id_pick(void) {
 	for (;;) {
 		const struct request *req = req_head, *started_at;
-#ifdef DNS_USE_CPU_CLOCK_FOR_ID
-		struct timespec ts;
-		u16 trans_id;
-#ifdef CLOCK_MONOTONIC
-		if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
-#else
-		if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
-#endif
-			event_err(1, "clock_gettime");
-                trans_id = ts.tv_nsec & 0xffff;
-#endif
-
-#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
-		struct timeval tv;
-		u16 trans_id;
-		gettimeofday(&tv, NULL);
-                trans_id = tv.tv_usec & 0xffff;
-#endif
-
-#ifdef DNS_USE_OPENSSL_FOR_ID
-		u16 trans_id;
-		if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
-			/* in the case that the RAND call fails we back */
-			/* down to using gettimeofday. */
-			struct timeval tv;
-			gettimeofday(&tv, NULL);
-			trans_id = tv.tv_usec & 0xffff; */
-			abort();
-		}
-#endif
+		u16 trans_id = trans_id_function();
 
 		if (trans_id == 0xffff) continue;
 		/* now check to see if that id is already inflight */
@@ -1472,7 +1511,7 @@
 		return -1;
 	}
 	item->type = type;
-	item->class = class;
+	item->dns_question_class = class;
 	item->ttl = ttl;
 	item->is_name = is_name != 0;
 	item->datalen = 0;
@@ -1587,7 +1626,7 @@
 			return (int) j;
 		}
 		APPEND16(req->base.questions[i]->type);
-		APPEND16(req->base.questions[i]->class);
+		APPEND16(req->base.questions[i]->dns_question_class);
 	}
 
 	/* Add answer, authority, and additional sections. */
@@ -1606,7 +1645,7 @@
 			j = r;
 
 			APPEND16(item->type);
-			APPEND16(item->class);
+			APPEND16(item->dns_question_class);
 			APPEND32(item->ttl);
 			if (item->is_name) {
 				off_t len_idx = j, name_start;
@@ -1616,7 +1655,7 @@
 				if (r < 0)
 					goto overflow;
 				j = r;
-				_t = htons( (j-name_start) );
+				_t = htons( (short) (j-name_start) );
 				memcpy(buf+len_idx, &_t, 2);
 			} else {
 				APPEND16(item->datalen);
@@ -1663,8 +1702,8 @@
 	r = sendto(port->socket, req->response, req->response_len, 0,
 			   (struct sockaddr*) &req->addr, req->addrlen);
 	if (r<0) {
-		int err = last_error(port->socket);
-		if (! error_is_eagain(err))
+		int sock_err = last_error(port->socket);
+		if (! error_is_eagain(sock_err))
 			return -1;
 
 		if (port->pending_replies) {
@@ -1981,7 +2020,8 @@
 	while (1) {
 		struct nameserver *next = server->next;
 		(void) event_del(&server->event);
-		(void) evtimer_del(&server->timeout_event);
+		if (evtimer_initialized(&server->timeout_event))
+			(void) evtimer_del(&server->timeout_event);
 		if (server->socket >= 0)
 			CLOSE_SOCKET(server->socket);
 		free(server);
@@ -2050,14 +2090,7 @@
 
 	ns->socket = socket(PF_INET, SOCK_DGRAM, 0);
 	if (ns->socket < 0) { err = 1; goto out1; }
-#ifdef WIN32
-        {
-		u_long nonblocking = 1;
-		ioctlsocket(ns->socket, FIONBIO, &nonblocking);
-	}
-#else
-        fcntl(ns->socket, F_SETFL, O_NONBLOCK);
-#endif
+        evutil_make_socket_nonblocking(ns->socket);
 	sin.sin_addr.s_addr = address;
 	sin.sin_port = htons(port);
 	sin.sin_family = AF_INET;
@@ -2267,7 +2300,8 @@
 }
 
 int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type
callback, void *ptr) {
-	char buf[64];
+	/* 32 nybbles, 32 periods, "ip6.arpa", NUL. */
+	char buf[73];
 	char *cp;
 	struct request *req;
 	int i;
@@ -2280,8 +2314,8 @@
 		*cp++ = "0123456789abcdef"[byte >> 4];
 		*cp++ = '.';
 	}
-	assert(cp + strlen(".ip6.arpa") < buf+sizeof(buf));
-	memcpy(cp, ".ip6.arpa", strlen(".ip6.arpa")+1);
+	assert(cp + strlen("ip6.arpa") < buf+sizeof(buf));
+	memcpy(cp, "ip6.arpa", strlen("ip6.arpa")+1);
 	log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
 	req = request_new(TYPE_PTR, buf, flags, callback, ptr);
 	if (!req) return 1;
@@ -2495,7 +2529,7 @@
 			/* this name without a postfix */
 			if (string_num_dots(req->search_origname) < req->search_state->ndots) {
 				/* yep, we need to try it raw */
-				struct request *const newreq = request_new(req->request_type,
req->search_origname, req->search_flags, req->user_callback,
req->user_pointer);
+				newreq = request_new(req->request_type, req->search_origname,
req->search_flags, req->user_callback, req->user_pointer);
 				log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
 				if (newreq) {
 					request_submit(newreq);
@@ -2917,7 +2951,7 @@
 #undef TRY
 }
 
-int
+static int
 evdns_config_windows_nameservers(void)
 {
 	if (load_nameservers_with_getnetworkparams() == 0)
@@ -2931,7 +2965,7 @@
 {
 	int res = 0;
 #ifdef WIN32
-	evdns_config_windows_nameservers();
+	res = evdns_config_windows_nameservers();
 #else
 	res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
 #endif
@@ -3029,20 +3063,20 @@
 	for (i = 0; i < req->nquestions; ++i) {
 		u32 ans = htonl(0xc0a80b0bUL);
 		if (req->questions[i]->type == EVDNS_TYPE_A &&
-			req->questions[i]->class == EVDNS_CLASS_INET) {
+			req->questions[i]->dns_question_class == EVDNS_CLASS_INET) {
 			printf(" -- replying for %s (A)\n", req->questions[i]->name);
 			r = evdns_server_request_add_a_reply(req, req->questions[i]->name,
 										  1, &ans, 10);
 			if (r<0)
 				printf("eeep, didn't work.\n");
 		} else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
-				   req->questions[i]->class == EVDNS_CLASS_INET) {
+				   req->questions[i]->dns_question_class == EVDNS_CLASS_INET) {
 			printf(" -- replying for %s (PTR)\n", req->questions[i]->name);
 			r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name,
 											"foo.bar.example.com", 10);
 		} else {
 			printf(" -- skipping %s [%d %d]\n", req->questions[i]->name,
-				   req->questions[i]->type, req->questions[i]->class);
+				   req->questions[i]->type, req->questions[i]->dns_question_class);
 		}
 	}
 
@@ -3085,7 +3119,7 @@
 		int sock;
 		struct sockaddr_in my_addr;
 		sock = socket(PF_INET, SOCK_DGRAM, 0);
-		fcntl(sock, F_SETFL, O_NONBLOCK);
+                evutil_make_socket_nonblocking(sock);
 		my_addr.sin_family = AF_INET;
 		my_addr.sin_port = htons(10053);
 		my_addr.sin_addr.s_addr = INADDR_ANY;

=== modified file 'extra/libevent/evdns.h'
--- a/extra/libevent/evdns.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/evdns.h	2008-04-28 17:52:20 +0000
@@ -47,7 +47,8 @@
  * the source verbatim in their source distributions)
  */
 
-/*
+/** @file evdns.h
+ *
  * Welcome, gentle reader
  *
  * Async DNS lookups are really a whole lot harder than they should be,
@@ -136,85 +137,6 @@
  *  Query: www.abc
  *  Order: www.abc., www.abc.myhome.net
  *
- * API reference:
- *
- * int evdns_nameserver_add(unsigned long int address)
- *   Add a nameserver. The address should be an IP address in
- *   network byte order. The type of address is chosen so that
- *   it matches in_addr.s_addr.
- *   Returns non-zero on error.
- *
- * int evdns_nameserver_ip_add(const char *ip_as_string)
- *   This wraps the above function by parsing a string as an IP
- *   address and adds it as a nameserver.
- *   Returns non-zero on error
- *
- * int evdns_resolve(const char *name, int flags,
- *		      evdns_callback_type callback,
- *		      void *ptr)
- *   Resolve a name. The name parameter should be a DNS name.
- *   The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
- *   which disables searching for this query. (see defn of
- *   searching above).
- *
- *   The callback argument is a function which is called when
- *   this query completes and ptr is an argument which is passed
- *   to that callback function.
- *
- *   Returns non-zero on error
- *
- * void evdns_search_clear()
- *   Clears the list of search domains
- *
- * void evdns_search_add(const char *domain)
- *   Add a domain to the list of search domains
- *
- * void evdns_search_ndots_set(int ndots)
- *   Set the number of dots which, when found in a name, causes
- *   the first query to be without any search domain.
- *
- * int evdns_count_nameservers(void)
- *   Return the number of configured nameservers (not necessarily the
- *   number of running nameservers).  This is useful for double-checking
- *   whether our calls to the various nameserver configuration functions
- *   have been successful.
- *
- * int evdns_clear_nameservers_and_suspend(void)
- *   Remove all currently configured nameservers, and suspend all pending
- *   resolves.  Resolves will not necessarily be re-attempted until
- *   evdns_resume() is called.
- *
- * int evdns_resume(void)
- *   Re-attempt resolves left in limbo after an earlier call to
- *   evdns_clear_nameservers_and_suspend().
- *
- * int evdns_config_windows_nameservers(void)
- *   Attempt to configure a set of nameservers based on platform settings on
- *   a win32 host.  Preferentially tries to use GetNetworkParams; if that fails,
- *   looks in the registry.  Returns 0 on success, nonzero on failure.
- *
- * int evdns_resolv_conf_parse(int flags, const char *filename)
- *   Parse a resolv.conf like file from the given filename.
- *
- *   See the man page for resolv.conf for the format of this file.
- *   The flags argument determines what information is parsed from
- *   this file:
- *     DNS_OPTION_SEARCH - domain, search and ndots options
- *     DNS_OPTION_NAMESERVERS - nameserver lines
- *     DNS_OPTION_MISC - timeout and attempts options
- *     DNS_OPTIONS_ALL - all of the above
- *   The following directives are not parsed from the file:
- *     sortlist, rotate, no-check-names, inet6, debug
- *
- *   Returns non-zero on error:
- *    0 no errors
- *    1 failed to open file
- *    2 failed to stat file
- *    3 file too large
- *    4 out of memory
- *    5 short read from file
- *    6 no nameservers in file
- *
  * Internals:
  *
  * Requests are kept in two queues. The first is the inflight queue. In
@@ -242,27 +164,30 @@
 extern "C" {
 #endif
 
-/* Error codes 0-5 are as described in RFC 1035. */
+/* For integer types. */
+#include <evutil.h>
+
+/** Error codes 0-5 are as described in RFC 1035. */
 #define DNS_ERR_NONE 0
-/* The name server was unable to interpret the query */
+/** The name server was unable to interpret the query */
 #define DNS_ERR_FORMAT 1
-/* The name server was unable to process this query due to a problem with the
+/** The name server was unable to process this query due to a problem with the
  * name server */
 #define DNS_ERR_SERVERFAILED 2
-/* The domain name does not exist */
+/** The domain name does not exist */
 #define DNS_ERR_NOTEXIST 3
-/* The name server does not support the requested kind of query */
+/** The name server does not support the requested kind of query */
 #define DNS_ERR_NOTIMPL 4
-/* The name server refuses to reform the specified operation for policy
+/** The name server refuses to reform the specified operation for policy
  * reasons */
 #define DNS_ERR_REFUSED 5
-/* The reply was truncated or ill-formated */
+/** The reply was truncated or ill-formated */
 #define DNS_ERR_TRUNCATED 65
-/* An unknown error occurred */
+/** An unknown error occurred */
 #define DNS_ERR_UNKNOWN 66
-/* Communication with the server timed out */
+/** Communication with the server timed out */
 #define DNS_ERR_TIMEOUT 67
-/* The request was canceled because the DNS subsystem was shut down. */
+/** The request was canceled because the DNS subsystem was shut down. */
 #define DNS_ERR_SHUTDOWN 68
 
 #define DNS_IPv4_A 1
@@ -276,7 +201,7 @@
 #define DNS_OPTION_MISC 4
 #define DNS_OPTIONS_ALL 7
 
-/* 
+/**
  * The callback that contains the results from a lookup.
  * - type is either DNS_IPv4_A or DNS_PTR or DNS_IPv6_AAAA
  * - count contains the number of addresses of form type
@@ -285,38 +210,262 @@
  */
 typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void
*addresses, void *arg);
 
+/**
+  Initialize the asynchronous DNS library.
+
+  This function initializes support for non-blocking name resolution by
+  calling evdns_resolv_conf_parse() on UNIX and
+  evdns_config_windows_nameservers() on Windows.
+
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_shutdown()
+ */
 int evdns_init(void);
+
+
+/**
+  Shut down the asynchronous DNS resolver and terminate all active requests.
+
+  If the 'fail_requests' option is enabled, all active requests will return
+  an empty result with the error flag set to DNS_ERR_SHUTDOWN. Otherwise,
+  the requests will be silently discarded.
+
+  @param fail_requests if zero, active requests will be aborted; if non-zero,
+		active requests will return DNS_ERR_SHUTDOWN.
+  @see evdns_init()
+ */
 void evdns_shutdown(int fail_requests);
+
+
+/**
+  Convert a DNS error code to a string.
+
+  @param err the DNS error code
+  @return a string containing an explanation of the error code
+*/
 const char *evdns_err_to_string(int err);
+
+
+/**
+  Add a nameserver.
+
+  The address should be an IPv4 address in network byte order.
+  The type of address is chosen so that it matches in_addr.s_addr.
+
+  @param address an IP address in network byte order
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_nameserver_ip_add()
+ */
 int evdns_nameserver_add(unsigned long int address);
+
+
+/**
+  Get the number of configured nameservers.
+
+  This returns the number of configured nameservers (not necessarily the
+  number of running nameservers).  This is useful for double-checking
+  whether our calls to the various nameserver configuration functions
+  have been successful.
+
+  @return the number of configured nameservers
+  @see evdns_nameserver_add()
+ */
 int evdns_count_nameservers(void);
+
+
+/**
+  Remove all configured nameservers, and suspend all pending resolves.
+
+  Resolves will not necessarily be re-attempted until evdns_resume() is called.
+
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resume()
+ */
 int evdns_clear_nameservers_and_suspend(void);
+
+
+/**
+  Resume normal operation and continue any suspended resolve requests.
+
+  Re-attempt resolves left in limbo after an earlier call to
+  evdns_clear_nameservers_and_suspend().
+
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_clear_nameservers_and_suspend()
+ */
 int evdns_resume(void);
+
+
+/**
+  Add a nameserver.
+
+  This wraps the evdns_nameserver_add() function by parsing a string as an IP
+  address and adds it as a nameserver.
+
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_nameserver_add()
+ */
 int evdns_nameserver_ip_add(const char *ip_as_string);
+
+
+/**
+  Lookup an A record for a given name.
+
+  @param name a DNS hostname
+  @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+  @param callback a callback function to invoke when the request is completed
+  @param ptr an argument to pass to the callback function
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resolve_ipv6(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
+ */
 int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void
*ptr);
+
+
+/**
+  Lookup an AAAA record for a given name.
+
+  @param name a DNS hostname
+  @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+  @param callback a callback function to invoke when the request is completed
+  @param ptr an argument to pass to the callback function
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resolve_ipv4(), evdns_resolve_reverse(), evdns_resolve_reverse_ipv6()
+ */
 int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void
*ptr);
+
 struct in_addr;
 struct in6_addr;
+
+/**
+  Lookup a PTR record for a given IP address.
+
+  @param in an IPv4 address
+  @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+  @param callback a callback function to invoke when the request is completed
+  @param ptr an argument to pass to the callback function
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resolve_reverse_ipv6()
+ */
 int evdns_resolve_reverse(struct in_addr *in, int flags, evdns_callback_type callback,
void *ptr);
+
+
+/**
+  Lookup a PTR record for a given IPv6 address.
+
+  @param in an IPv6 address
+  @param flags either 0, or DNS_QUERY_NO_SEARCH to disable searching for this query.
+  @param callback a callback function to invoke when the request is completed
+  @param ptr an argument to pass to the callback function
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resolve_reverse_ipv6()
+ */
 int evdns_resolve_reverse_ipv6(struct in6_addr *in, int flags, evdns_callback_type
callback, void *ptr);
+
+
+/**
+  Set the value of a configuration option.
+
+  The currently available configuration options are:
+
+    ndots, timeout, max-timeouts, max-inflight, and attempts
+
+  @param option the name of the configuration option to be modified
+  @param val the value to be set
+  @param flags either 0 | DNS_OPTION_SEARCH | DNS_OPTION_MISC
+  @return 0 if successful, or -1 if an error occurred
+ */
 int evdns_set_option(const char *option, const char *val, int flags);
-int evdns_resolv_conf_parse(int flags, const char *);
+
+
+/**
+  Parse a resolv.conf file.
+
+  The 'flags' parameter determines what information is parsed from the
+  resolv.conf file. See the man page for resolv.conf for the format of this
+  file.
+
+  The following directives are not parsed from the file: sortlist, rotate,
+  no-check-names, inet6, debug.
+
+  If this function encounters an error, the possible return values are: 1 =
+  failed to open file, 2 = failed to stat file, 3 = file too large, 4 = out of
+  memory, 5 = short read from file, 6 = no nameservers listed in the file
+
+  @param flags any of DNS_OPTION_NAMESERVERS|DNS_OPTION_SEARCH|DNS_OPTION_MISC|
+         DNS_OPTIONS_ALL
+  @param filename the path to the resolv.conf file
+  @return 0 if successful, or various positive error codes if an error
+          occurred (see above)
+  @see resolv.conf(3), evdns_config_windows_nameservers()
+ */
+int evdns_resolv_conf_parse(int flags, const char *const filename);
+
+
+/**
+  Obtain nameserver information using the Windows API.
+
+  Attempt to configure a set of nameservers based on platform settings on
+  a win32 host.  Preferentially tries to use GetNetworkParams; if that fails,
+  looks in the registry.
+
+  @return 0 if successful, or -1 if an error occurred
+  @see evdns_resolv_conf_parse()
+ */
 #ifdef MS_WINDOWS
 int evdns_config_windows_nameservers(void);
 #endif
+
+
+/**
+  Clear the list of search domains.
+ */
 void evdns_search_clear(void);
+
+
+/**
+  Add a domain to the list of search domains
+
+  @param domain the domain to be added to the search list
+ */
 void evdns_search_add(const char *domain);
+
+
+/**
+  Set the 'ndots' parameter for searches.
+
+  Sets the number of dots which, when found in a name, causes
+  the first query to be without any search domain.
+
+  @param ndots the new ndots parameter
+ */
 void evdns_search_ndots_set(const int ndots);
 
+/**
+  A callback that is invoked when a log message is generated
+
+  @param is_warning indicates if the log message is a 'warning'
+  @param msg the content of the log message
+ */
 typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
+
+
+/**
+  Set the callback function to handle log messages.
+
+  @param fn the callback to be invoked when a log message is generated
+ */
 void evdns_set_log_fn(evdns_debug_log_fn_type fn);
 
+/**
+   Set a callback that will be invoked to generate transaction IDs.  By
+   default, we pick transaction IDs based on the current clock time.
+
+   @param fn the new callback, or NULL to use the default.
+ */
+void evdns_set_transaction_id_fn(ev_uint16_t (*fn)(void));
+
 #define DNS_NO_SEARCH 1
 
-#ifdef __cplusplus
-}
-#endif
-
 /*
  * Structures and functions used to implement a DNS server.
  */
@@ -328,7 +477,15 @@
 };
 struct evdns_server_question {
 	int type;
+#ifdef __cplusplus
+	int dns_question_class;
+#else
+	/* You should refer to this field as "dns_question_class".  The
+	 * name "class" works in C for backward compatibility, and will be
+	 * removed in a future version. (1.5 or later). */
 	int class;
+#define dns_question_class class
+#endif
 	char name[1];
 };
 typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
@@ -353,7 +510,7 @@
 struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp,
evdns_request_callback_fn_type callback, void *user_data);
 void evdns_close_server_port(struct evdns_server_port *port);
 
-int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const
char *name, int type, int class, int ttl, int datalen, int is_name, const char *data);
+int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const
char *name, int type, int dns_class, int ttl, int datalen, int is_name, const char
*data);
 int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name,
int n, void *addrs, int ttl);
 int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char
*name, int n, void *addrs, int ttl);
 int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr
*in, const char *inaddr_name, const char *hostname, int ttl);
@@ -364,4 +521,8 @@
 struct sockaddr;
 int evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct
sockaddr *sa, int addr_len);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif  /* !EVENTDNS_H */

=== modified file 'extra/libevent/event-internal.h'
--- a/extra/libevent/event-internal.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/event-internal.h	2008-04-28 17:52:20 +0000
@@ -31,8 +31,21 @@
 extern "C" {
 #endif
 
+#include "config.h"
+#include "min_heap.h"
 #include "evsignal.h"
 
+struct eventop {
+	const char *name;
+	void *(*init)(struct event_base *);
+	int (*add)(void *, struct event *);
+	int (*del)(void *, struct event *);
+	int (*dispatch)(struct event_base *, void *, struct timeval *);
+	void (*dealloc)(struct event_base *, void *);
+	/* set if we need to reinitialize the event base */
+	int need_reinit;
+};
+
 struct event_base {
 	const struct eventop *evsel;
 	void *evbase;
@@ -40,6 +53,7 @@
 	int event_count_active;	/* counts number of active events */
 
 	int event_gotterm;		/* Set to terminate loop */
+	int event_break;		/* Set to terminate loop immediately */
 
 	/* active event management */
 	struct event_list **activequeues;
@@ -51,9 +65,30 @@
 	struct event_list eventqueue;
 	struct timeval event_tv;
 
-	RB_HEAD(event_tree, event) timetree;
+	struct min_heap timeheap;
 };
 
+/* Internal use only: Functions that might be missing from <sys/queue.h> */
+#ifndef HAVE_TAILQFOREACH
+#define	TAILQ_FIRST(head)		((head)->tqh_first)
+#define	TAILQ_END(head)			NULL
+#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
+#define TAILQ_FOREACH(var, head, field)					\
+	for((var) = TAILQ_FIRST(head);					\
+	    (var) != TAILQ_END(head);					\
+	    (var) = TAILQ_NEXT(var, field))
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	(elm)->field.tqe_next = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
+} while (0)
+#endif /* TAILQ_FOREACH */
+
+int _evsignal_set_handler(struct event_base *base, int evsignal,
+			  void (*fn)(int));
+int _evsignal_restore_handler(struct event_base *base, int evsignal);
+
 #ifdef __cplusplus
 }
 #endif

=== modified file 'extra/libevent/event.c'
--- a/extra/libevent/event.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/event.c	2008-04-28 17:52:20 +0000
@@ -35,7 +35,6 @@
 #include "misc.h"
 #endif
 #include <sys/types.h>
-#include <sys/tree.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #else 
@@ -55,6 +54,7 @@
 
 #include "event.h"
 #include "event-internal.h"
+#include "evutil.h"
 #include "log.h"
 
 #ifdef HAVE_EVENT_PORTS
@@ -66,9 +66,6 @@
 #ifdef HAVE_POLL
 extern const struct eventop pollops;
 #endif
-#ifdef HAVE_RTSIG
-extern const struct eventop rtsigops;
-#endif
 #ifdef HAVE_EPOLL
 extern const struct eventop epollops;
 #endif
@@ -96,9 +93,6 @@
 #ifdef HAVE_DEVPOLL
 	&devpollops,
 #endif
-#ifdef HAVE_RTSIG
-	&rtsigops,
-#endif
 #ifdef HAVE_POLL
 	&pollops,
 #endif
@@ -131,20 +125,6 @@
 static void	timeout_process(struct event_base *);
 static void	timeout_correct(struct event_base *, struct timeval *);
 
-static int
-compare(struct event *a, struct event *b)
-{
-	if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
-		return (-1);
-	else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
-		return (1);
-	if (a < b)
-		return (-1);
-	else if (a > b)
-		return (1);
-	return (0);
-}
-
 static void
 detect_monotonic(void)
 {
@@ -175,19 +155,25 @@
 	return (gettimeofday(tp, NULL));
 }
 
-RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
-
-RB_GENERATE(event_tree, event, ev_timeout_node, compare);
-
-
-void *
+struct event_base *
 event_init(void)
 {
+	struct event_base *base = event_base_new();
+
+	if (base != NULL)
+		current_base = base;
+
+	return (base);
+}
+
+struct event_base *
+event_base_new(void)
+{
 	int i;
 	struct event_base *base;
 
 	if ((base = calloc(1, sizeof(struct event_base))) == NULL)
-		event_err(1, "%s: calloc");
+		event_err(1, "%s: calloc", __func__);
 
 	event_sigcb = NULL;
 	event_gotsig = 0;
@@ -195,7 +181,7 @@
 	detect_monotonic();
 	gettime(&base->event_tv);
 	
-	RB_INIT(&base->timetree);
+	min_heap_ctor(&base->timeheap);
 	TAILQ_INIT(&base->eventqueue);
 	TAILQ_INIT(&base->sig.signalqueue);
 	base->sig.ev_signal_pair[0] = -1;
@@ -218,27 +204,48 @@
 	/* allocate a single active event queue */
 	event_base_priority_init(base, 1);
 
-	current_base = base;
 	return (base);
 }
 
 void
 event_base_free(struct event_base *base)
 {
-	int i;
+	int i, n_deleted=0;
+	struct event *ev;
 
 	if (base == NULL && current_base)
 		base = current_base;
-        if (base == current_base)
+	if (base == current_base)
 		current_base = NULL;
 
+	/* XXX(niels) - check for internal events first */
 	assert(base);
+	/* Delete all non-internal events. */
+	for (ev = TAILQ_FIRST(&base->eventqueue); ev; ) {
+		struct event *next = TAILQ_NEXT(ev, ev_next);
+		if (!(ev->ev_flags & EVLIST_INTERNAL)) {
+			event_del(ev);
+			++n_deleted;
+		}
+		ev = next;
+	}
+	while ((ev = min_heap_top(&base->timeheap)) != NULL) {
+		event_del(ev);
+		++n_deleted;
+	}
+
+	if (n_deleted)
+		event_debug(("%s: %d events were still set in base",
+					 __func__, n_deleted));
+
 	if (base->evsel->dealloc != NULL)
 		base->evsel->dealloc(base, base->evbase);
-	for (i=0; i < base->nactivequeues; ++i)
+
+	for (i = 0; i < base->nactivequeues; ++i)
 		assert(TAILQ_EMPTY(base->activequeues[i]));
 
-	assert(RB_EMPTY(&base->timetree));
+	assert(min_heap_empty(&base->timeheap));
+	min_heap_dtor(&base->timeheap);
 
 	for (i = 0; i < base->nactivequeues; ++i)
 		free(base->activequeues[i]);
@@ -249,6 +256,34 @@
 	free(base);
 }
 
+/* reinitialized the event base after a fork */
+int
+event_reinit(struct event_base *base)
+{
+	const struct eventop *evsel = base->evsel;
+	void *evbase = base->evbase;
+	int res = 0;
+	struct event *ev;
+
+	/* check if this event mechanism requires reinit */
+	if (!evsel->need_reinit)
+		return (0);
+
+	if (base->evsel->dealloc != NULL)
+		base->evsel->dealloc(base, base->evbase);
+	base->evbase = evsel->init(base);
+	if (base->evbase == NULL)
+		event_errx(1, "%s: could not reinitialize event mechanism",
+		    __func__);
+
+	TAILQ_FOREACH(ev, &base->eventqueue, ev_next) {
+		if (evsel->add(evbase, ev) == -1)
+			res = -1;
+	}
+
+	return (res);
+}
+
 int
 event_priority_init(int npriorities)
 {
@@ -307,9 +342,6 @@
 	int i;
 	short ncalls;
 
-	if (!base->event_count_active)
-		return;
-
 	for (i = 0; i < base->nactivequeues; ++i) {
 		if (TAILQ_FIRST(base->activequeues[i]) != NULL) {
 			activeq = base->activequeues[i];
@@ -320,7 +352,10 @@
 	assert(activeq != NULL);
 
 	for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
-		event_queue_remove(base, ev, EVLIST_ACTIVE);
+		if (ev->ev_events & EV_PERSIST)
+			event_queue_remove(base, ev, EVLIST_ACTIVE);
+		else
+			event_del(ev);
 		
 		/* Allows deletes to work */
 		ncalls = ev->ev_ncalls;
@@ -329,7 +364,7 @@
 			ncalls--;
 			ev->ev_ncalls = ncalls;
 			(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
-			if (event_gotsig)
+			if (event_gotsig || base->event_break)
 				return;
 		}
 	}
@@ -351,6 +386,13 @@
   return (event_base_loop(event_base, 0));
 }
 
+const char *
+event_base_get_method(struct event_base *base)
+{
+	assert(base);
+	return (base->evsel->name);
+}
+
 static void
 event_loopexit_cb(int fd, short what, void *arg)
 {
@@ -374,6 +416,25 @@
 }
 
 /* not thread safe */
+int
+event_loopbreak(void)
+{
+	return (event_base_loopbreak(current_base));
+}
+
+int
+event_base_loopbreak(struct event_base *event_base)
+{
+	if (event_base == NULL)
+		return (-1);
+
+	event_base->event_break = 1;
+	return (0);
+}
+
+
+
+/* not thread safe */
 
 int
 event_loop(int flags)
@@ -390,22 +451,21 @@
 	struct timeval *tv_p;
 	int res, done;
 
-#ifndef WIN32
 	if(!TAILQ_EMPTY(&base->sig.signalqueue))
 		evsignal_base = base;
-#endif
 	done = 0;
 	while (!done) {
-		/* Calculate the initial events that we are waiting for */
-		if (evsel->recalc(base, evbase, 0) == -1)
-			return (-1);
-
 		/* Terminate the loop if we have been asked to */
 		if (base->event_gotterm) {
 			base->event_gotterm = 0;
 			break;
 		}
 
+		if (base->event_break) {
+			base->event_break = 0;
+			break;
+		}
+
 		/* You cannot use this interface for multi-threaded apps */
 		while (event_gotsig) {
 			event_gotsig = 0;
@@ -428,7 +488,7 @@
 			 * if we have active events, we just poll new events
 			 * without waiting.
 			 */
-			timerclear(&tv);
+			evutil_timerclear(&tv);
 		}
 		
 		/* If we have no events, we just exit */
@@ -439,7 +499,6 @@
 
 		res = evsel->dispatch(base, evbase, tv_p);
 
-
 		if (res == -1)
 			return (-1);
 
@@ -506,7 +565,7 @@
 
 	if (events == EV_TIMEOUT) {
 		if (tv == NULL) {
-			timerclear(&etv);
+			evutil_timerclear(&etv);
 			tv = &etv;
 		}
 
@@ -548,6 +607,8 @@
 	ev->ev_ncalls = 0;
 	ev->ev_pncalls = NULL;
 
+	min_heap_elem_init(ev);
+
 	/* by default, we put new events into the middle priority */
 	if(current_base)
 		ev->ev_pri = current_base->nactivequeues/2;
@@ -608,10 +669,10 @@
 	/* See if there is a timeout that we should report */
 	if (tv != NULL && (flags & event & EV_TIMEOUT)) {
 		gettime(&now);
-		timersub(&ev->ev_timeout, &now, &res);
+		evutil_timersub(&ev->ev_timeout, &now, &res);
 		/* correctly remap to real time */
 		gettimeofday(&now, NULL);
-		timeradd(&now, &res, tv);
+		evutil_timeradd(&now, &res, tv);
 	}
 
 	return (flags & event);
@@ -639,6 +700,9 @@
 
 		if (ev->ev_flags & EVLIST_TIMEOUT)
 			event_queue_remove(base, ev, EVLIST_TIMEOUT);
+		else if (min_heap_reserve(&base->timeheap,
+			1 + min_heap_size(&base->timeheap)) == -1)
+		    return (-1);  /* ENOMEM == errno */
 
 		/* Check if it is active due to a timeout.  Rescheduling
 		 * this timeout before the callback can be executed
@@ -657,7 +721,7 @@
 		}
 
 		gettime(&now);
-		timeradd(&now, tv, &ev->ev_timeout);
+		evutil_timeradd(&now, tv, &ev->ev_timeout);
 
 		event_debug((
 			 "event_add: timeout in %d seconds, call %p",
@@ -668,14 +732,18 @@
 
 	if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
 	    !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
-		event_queue_insert(base, ev, EVLIST_INSERTED);
+		int res = evsel->add(evbase, ev);
+		if (res != -1)
+			event_queue_insert(base, ev, EVLIST_INSERTED);
 
-		return (evsel->add(evbase, ev));
+		return (res);
 	} else if ((ev->ev_events & EV_SIGNAL) &&
 	    !(ev->ev_flags & EVLIST_SIGNAL)) {
-		event_queue_insert(base, ev, EVLIST_SIGNAL);
+		int res = evsel->add(evbase, ev);
+		if (res != -1)
+			event_queue_insert(base, ev, EVLIST_SIGNAL);
 
-		return (evsel->add(evbase, ev));
+		return (res);
 	}
 
 	return (0);
@@ -746,7 +814,7 @@
 	struct event *ev;
 	struct timeval *tv = *tv_p;
 
-	if ((ev = RB_MIN(event_tree, &base->timetree)) == NULL) {
+	if ((ev = min_heap_top(&base->timeheap)) == NULL) {
 		/* if no time-based events are active wait for I/O */
 		*tv_p = NULL;
 		return (0);
@@ -755,12 +823,12 @@
 	if (gettime(&now) == -1)
 		return (-1);
 
-	if (timercmp(&ev->ev_timeout, &now, <=)) {
-		timerclear(tv);
+	if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
+		evutil_timerclear(tv);
 		return (0);
 	}
 
-	timersub(&ev->ev_timeout, &now, tv);
+	evutil_timersub(&ev->ev_timeout, &now, tv);
 
 	assert(tv->tv_sec >= 0);
 	assert(tv->tv_usec >= 0);
@@ -778,7 +846,8 @@
 static void
 timeout_correct(struct event_base *base, struct timeval *tv)
 {
-	struct event *ev;
+	struct event **pev;
+	unsigned int size;
 	struct timeval off;
 
 	if (use_monotonic)
@@ -786,37 +855,41 @@
 
 	/* Check if time is running backwards */
 	gettime(tv);
-	if (timercmp(tv, &base->event_tv, >=)) {
+	if (evutil_timercmp(tv, &base->event_tv, >=)) {
 		base->event_tv = *tv;
 		return;
 	}
 
 	event_debug(("%s: time is running backwards, corrected",
 		    __func__));
-	timersub(&base->event_tv, tv, &off);
+	evutil_timersub(&base->event_tv, tv, &off);
 
 	/*
 	 * We can modify the key element of the node without destroying
 	 * the key, beause we apply it to all in the right order.
 	 */
-	RB_FOREACH(ev, event_tree, &base->timetree)
-		timersub(&ev->ev_timeout, &off, &ev->ev_timeout);
+	pev = base->timeheap.p;
+	size = base->timeheap.n;
+	for (; size-- > 0; ++pev) {
+		struct timeval *ev_tv = &(**pev).ev_timeout;
+		evutil_timersub(ev_tv, &off, ev_tv);
+	}
 }
 
 void
 timeout_process(struct event_base *base)
 {
 	struct timeval now;
-	struct event *ev, *next;
+	struct event *ev;
+
+	if (min_heap_empty(&base->timeheap))
+		return;
 
 	gettime(&now);
 
-	for (ev = RB_MIN(event_tree, &base->timetree); ev; ev = next) {
-		if (timercmp(&ev->ev_timeout, &now, >))
+	while ((ev = min_heap_top(&base->timeheap))) {
+		if (evutil_timercmp(&ev->ev_timeout, &now, >))
 			break;
-		next = RB_NEXT(event_tree, &base->timetree, ev);
-
-		event_queue_remove(base, ev, EVLIST_TIMEOUT);
 
 		/* delete this event from the I/O queues */
 		event_del(ev);
@@ -830,23 +903,17 @@
 void
 event_queue_remove(struct event_base *base, struct event *ev, int queue)
 {
-	int docount = 1;
-
 	if (!(ev->ev_flags & queue))
 		event_errx(1, "%s: %p(fd %d) not on queue %x", __func__,
 			   ev, ev->ev_fd, queue);
 
-	if (ev->ev_flags & EVLIST_INTERNAL)
-		docount = 0;
-
-	if (docount)
+	if (~ev->ev_flags & EVLIST_INTERNAL)
 		base->event_count--;
 
 	ev->ev_flags &= ~queue;
 	switch (queue) {
 	case EVLIST_ACTIVE:
-		if (docount)
-			base->event_count_active--;
+		base->event_count_active--;
 		TAILQ_REMOVE(base->activequeues[ev->ev_pri],
 		    ev, ev_active_next);
 		break;
@@ -854,7 +921,7 @@
 		TAILQ_REMOVE(&base->sig.signalqueue, ev, ev_signal_next);
 		break;
 	case EVLIST_TIMEOUT:
-		RB_REMOVE(event_tree, &base->timetree, ev);
+		min_heap_erase(&base->timeheap, ev);
 		break;
 	case EVLIST_INSERTED:
 		TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
@@ -867,8 +934,6 @@
 void
 event_queue_insert(struct event_base *base, struct event *ev, int queue)
 {
-	int docount = 1;
-
 	if (ev->ev_flags & queue) {
 		/* Double insertion is possible for active events */
 		if (queue & EVLIST_ACTIVE)
@@ -878,17 +943,13 @@
 			   ev, ev->ev_fd, queue);
 	}
 
-	if (ev->ev_flags & EVLIST_INTERNAL)
-		docount = 0;
-
-	if (docount)
+	if (~ev->ev_flags & EVLIST_INTERNAL)
 		base->event_count++;
 
 	ev->ev_flags |= queue;
 	switch (queue) {
 	case EVLIST_ACTIVE:
-		if (docount)
-			base->event_count_active++;
+		base->event_count_active++;
 		TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
 		    ev,ev_active_next);
 		break;
@@ -896,8 +957,7 @@
 		TAILQ_INSERT_TAIL(&base->sig.signalqueue, ev, ev_signal_next);
 		break;
 	case EVLIST_TIMEOUT: {
-		struct event *tmp = RB_INSERT(event_tree, &base->timetree, ev);
-		assert(tmp == NULL);
+		min_heap_push(&base->timeheap, ev);
 		break;
 	}
 	case EVLIST_INSERTED:

=== modified file 'extra/libevent/event.h'
--- a/extra/libevent/event.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/event.h	2008-04-28 17:52:20 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * Copyright (c) 2000-2007 Niels Provos <provos@stripped>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,14 +27,153 @@
 #ifndef _EVENT_H_
 #define _EVENT_H_
 
+/** @mainpage
+
+  @section intro Introduction
+
+  libevent is an event notification library for developing scalable network
+  servers.  The libevent API provides a mechanism to execute a callback
+  function when a specific event occurs on a file descriptor or after a
+  timeout has been reached. Furthermore, libevent also support callbacks due
+  to signals or regular timeouts.
+
+  libevent is meant to replace the event loop found in event driven network
+  servers. An application just needs to call event_dispatch() and then add or
+  remove events dynamically without having to change the event loop.
+
+  Currently, libevent supports /dev/poll, kqueue(2), select(2), poll(2) and
+  epoll(4). It also has experimental support for real-time signals. The
+  internal event mechanism is completely independent of the exposed event API,
+  and a simple update of libevent can provide new functionality without having
+  to redesign the applications. As a result, Libevent allows for portable
+  application development and provides the most scalable event notification
+  mechanism available on an operating system. Libevent can also be used for
+  multi-threaded aplications; see Steven Grimm's explanation. Libevent should
+  compile on Linux, *BSD, Mac OS X, Solaris and Windows.
+
+  @section usage Standard usage
+
+  Every program that uses libevent must include the <event.h> header, and pass
+  the -levent flag to the linker.  Before using any of the functions in the
+  library, you must call event_init() or event_base_new() to perform one-time
+  initialization of the libevent library.
+
+  @section event Event notification
+
+  For each file descriptor that you wish to monitor, you must declare an event
+  structure and call event_set() to initialize the members of the structure.
+  To enable notification, you add the structure to the list of monitored
+  events by calling event_add().  The event structure must remain allocated as
+  long as it is active, so it should be allocated on the heap. Finally, you
+  call event_dispatch() to loop and dispatch events.
+
+  @section bufferevent I/O Buffers
+
+  libevent provides an abstraction on top of the regular event callbacks. This
+  abstraction is called a buffered event. A buffered event provides input and
+  output buffers that get filled and drained automatically. The user of a
+  buffered event no longer deals directly with the I/O, but instead is reading
+  from input and writing to output buffers.
+
+  Once initialized via bufferevent_new(), the bufferevent structure can be
+  used repeatedly with bufferevent_enable() and bufferevent_disable().
+  Instead of reading and writing directly to a socket, you would call
+  bufferevent_read() and bufferevent_write().
+
+  When read enabled the bufferevent will try to read from the file descriptor
+  and call the read callback. The write callback is executed whenever the
+  output buffer is drained below the write low watermark, which is 0 by
+  default.
+
+  @section timers Timers
+
+  libevent can also be used to create timers that invoke a callback after a
+  certain amount of time has expired. The evtimer_set() function prepares an
+  event struct to be used as a timer. To activate the timer, call
+  evtimer_add(). Timers can be deactivated by calling evtimer_del().
+
+  @section timeouts Timeouts
+
+  In addition to simple timers, libevent can assign timeout events to file
+  descriptors that are triggered whenever a certain amount of time has passed
+  with no activity on a file descriptor.  The timeout_set() function
+  initializes an event struct for use as a timeout. Once initialized, the
+  event must be activated by using timeout_add().  To cancel the timeout, call
+  timeout_del().
+
+  @section evdns Asynchronous DNS resolution
+
+  libevent provides an asynchronous DNS resolver that should be used instead
+  of the standard DNS resolver functions.  These functions can be imported by
+  including the <evdns.h> header in your program. Before using any of the
+  resolver functions, you must call evdns_init() to initialize the library. To
+  convert a hostname to an IP address, you call the evdns_resolve_ipv4()
+  function.  To perform a reverse lookup, you would call the
+  evdns_resolve_reverse() function.  All of these functions use callbacks to
+  avoid blocking while the lookup is performed.
+
+  @section evhttp Event-driven HTTP servers
+
+  libevent provides a very simple event-driven HTTP server that can be
+  embedded in your program and used to service HTTP requests.
+
+  To use this capability, you need to include the <evhttp.h> header in your
+  program.  You create the server by calling evhttp_new(). Add addresses and
+  ports to listen on with evhttp_bind_socket(). You then register one or more
+  callbacks to handle incoming requests.  Each URI can be assigned a callback
+  via the evhttp_set_cb() function.  A generic callback function can also be
+  registered via evhttp_set_gencb(); this callback will be invoked if no other
+  callbacks have been registered for a given URI.
+
+  @section evrpc A framework for RPC servers and clients
+ 
+  libevents provides a framework for creating RPC servers and clients.  It
+  takes care of marshaling and unmarshaling all data structures.
+
+  @section api API Reference
+
+  To browse the complete documentation of the libevent API, click on any of
+  the following links.
+
+  event.h
+  The primary libevent header
+
+  evdns.h
+  Asynchronous DNS resolution
+
+  evhttp.h
+  An embedded libevent-based HTTP server
+
+  evrpc.h
+  A framework for creating RPC servers and clients
+
+ */
+
+/** @file event.h
+
+  A library for writing event-driven network servers
+
+ */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#include <event-config.h>
+#ifdef _EVENT_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef _EVENT_HAVE_SYS_TIME_H
 #include <sys/time.h>
+#endif
+#ifdef _EVENT_HAVE_STDINT_H
 #include <stdint.h>
+#endif
 #include <stdarg.h>
 
+/* For int types. */
+#include <evutil.h>
+
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
@@ -68,25 +207,16 @@
 	struct type **tqe_prev;	/* address of previous next element */	\
 }
 #endif /* !TAILQ_ENTRY */
-#ifndef RB_ENTRY
-#define _EVENT_DEFINED_RBENTRY
-#define RB_ENTRY(type)							\
-struct {								\
-	struct type *rbe_left;		/* left element */		\
-	struct type *rbe_right;		/* right element */		\
-	struct type *rbe_parent;	/* parent element */		\
-	int rbe_color;			/* node color */		\
-}
-#endif /* !RB_ENTRY */
 
 struct event_base;
 struct event {
 	TAILQ_ENTRY (event) ev_next;
 	TAILQ_ENTRY (event) ev_active_next;
 	TAILQ_ENTRY (event) ev_signal_next;
-	RB_ENTRY (event) ev_timeout_node;
+	unsigned int min_heap_idx;	/* for managing timeouts */
 
 	struct event_base *ev_base;
+
 	int ev_fd;
 	short ev_events;
 	short ev_ncalls;
@@ -126,52 +256,253 @@
 TAILQ_HEAD (event_list, event);
 TAILQ_HEAD (evkeyvalq, evkeyval);
 #endif /* _EVENT_DEFINED_TQENTRY */
-#ifdef _EVENT_DEFINED_RBENTRY
-#undef RB_ENTRY
-#undef _EVENT_DEFINED_RBENTRY
-#endif /* _EVENT_DEFINED_RBENTRY */
-
-struct eventop {
-	char *name;
-	void *(*init)(struct event_base *);
-	int (*add)(void *, struct event *);
-	int (*del)(void *, struct event *);
-	int (*recalc)(struct event_base *, void *, int);
-	int (*dispatch)(struct event_base *, void *, struct timeval *);
-	void (*dealloc)(struct event_base *, void *);
-};
-
-void *event_init(void);
+
+/**
+  Initialize the event API.
+
+  Use event_base_new() to initialize a new event base, but does not set
+  the current_base global.   If using only event_base_new(), each event
+  added must have an event base set with event_base_set()
+
+  @see event_base_set(), event_base_free(), event_init()
+ */
+struct event_base *event_base_new(void);
+
+/**
+  Initialize the event API.
+
+  The event API needs to be initialized with event_init() before it can be
+  used.  Sets the current_base global representing the default base for
+  events that have no base associated with them.
+
+  @see event_base_set(), event_base_new()
+ */
+struct event_base *event_init(void);
+
+/**
+  Reinitialized the event base after a fork
+
+  Some event mechanisms do not survive across fork.   The event base needs
+  to be reinitialized with the event_reinit() function.
+
+  @param base the event base that needs to be re-initialized
+  @return 0 if successful, or -1 if some events could not be re-added.
+  @see event_base_new(), event_init()
+*/
+int event_reinit(struct event_base *base);
+
+/**
+  Loop to process events.
+
+  In order to process events, an application needs to call
+  event_dispatch().  This function only returns on error, and should
+  replace the event core of the application program.
+
+  @see event_base_dispatch()
+ */
 int event_dispatch(void);
+
+
+/**
+  Threadsafe event dispatching loop.
+
+  @param eb the event_base structure returned by event_init()
+  @see event_init(), event_dispatch()
+ */
 int event_base_dispatch(struct event_base *);
+
+
+/**
+ Get the kernel event notification mechanism used by libevent.
+ 
+ @param eb the event_base structure returned by event_base_new()
+ @return a string identifying the kernel event mechanism (kqueue, epoll, etc.)
+ */
+const char *event_base_get_method(struct event_base *);
+        
+        
+/**
+  Deallocate all memory associated with an event_base, and free the base.
+
+  Note that this function will not close any fds or free any memory passed
+  to event_set as the argument to callback.
+
+  @param eb an event_base to be freed
+ */
 void event_base_free(struct event_base *);
 
+
 #define _EVENT_LOG_DEBUG 0
 #define _EVENT_LOG_MSG   1
 #define _EVENT_LOG_WARN  2
 #define _EVENT_LOG_ERR   3
 typedef void (*event_log_cb)(int severity, const char *msg);
+/**
+  Redirect libevent's log messages.
+
+  @param cb a function taking two arguments: an integer severity between
+     _EVENT_LOG_DEBUG and _EVENT_LOG_ERR, and a string.  If cb is NULL,
+	 then the default log is used.
+  */
 void event_set_log_callback(event_log_cb cb);
 
-/* Associate a different event base with an event */
+/**
+  Associate a different event base with an event.
+
+  @param eb the event base
+  @param ev the event
+ */
 int event_base_set(struct event_base *, struct event *);
 
-#define EVLOOP_ONCE	0x01
-#define EVLOOP_NONBLOCK	0x02
+/**
+ event_loop() flags
+ */
+/*@{*/
+#define EVLOOP_ONCE	0x01	/**< Block at most once. */
+#define EVLOOP_NONBLOCK	0x02	/**< Do not block. */
+/*@}*/
+
+/**
+  Handle events.
+
+  This is a more flexible version of event_dispatch().
+
+  @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK
+  @return 0 if successful, -1 if an error occurred, or 1 if no events were
+    registered.
+  @see event_loopexit(), event_base_loop()
+*/
 int event_loop(int);
+
+/**
+  Handle events (threadsafe version).
+
+  This is a more flexible version of event_base_dispatch().
+
+  @param eb the event_base structure returned by event_init()
+  @param flags any combination of EVLOOP_ONCE | EVLOOP_NONBLOCK
+  @return 0 if successful, -1 if an error occurred, or 1 if no events were
+    registered.
+  @see event_loopexit(), event_base_loop()
+  */
 int event_base_loop(struct event_base *, int);
-int event_loopexit(struct timeval *);	/* Causes the loop to exit */
+
+/**
+  Exit the event loop after the specified time.
+
+  The next event_loop() iteration after the given timer expires will
+  complete normally (handling all queued events) then exit without
+  blocking for events again.
+
+  Subsequent invocations of event_loop() will proceed normally.
+
+  @param tv the amount of time after which the loop should terminate.
+  @return 0 if successful, or -1 if an error occurred
+  @see event_loop(), event_base_loop(), event_base_loopexit()
+  */
+int event_loopexit(struct timeval *);
+
+
+/**
+  Exit the event loop after the specified time (threadsafe variant).
+
+  The next event_base_loop() iteration after the given timer expires will
+  complete normally (handling all queued events) then exit without
+  blocking for events again.
+
+  Subsequent invocations of event_base_loop() will proceed normally.
+
+  @param eb the event_base structure returned by event_init()
+  @param tv the amount of time after which the loop should terminate.
+  @return 0 if successful, or -1 if an error occurred
+  @see event_loopexit()
+ */
 int event_base_loopexit(struct event_base *, struct timeval *);
 
+/**
+  Abort the active event_loop() immediately.
+
+  event_loop() will abort the loop after the next event is completed;
+  event_loopbreak() is typically invoked from this event's callback.
+  This behavior is analogous to the "break;" statement.
+
+  Subsequent invocations of event_loop() will proceed normally.
+
+  @return 0 if successful, or -1 if an error occurred
+  @see event_base_loopbreak(), event_loopexit()
+ */
+int event_loopbreak(void);
+
+/**
+  Abort the active event_base_loop() immediately.
+
+  event_base_loop() will abort the loop after the next event is completed;
+  event_base_loopbreak() is typically invoked from this event's callback.
+  This behavior is analogous to the "break;" statement.
+
+  Subsequent invocations of event_loop() will proceed normally.
+
+  @param eb the event_base structure returned by event_init()
+  @return 0 if successful, or -1 if an error occurred
+  @see event_base_loopexit
+ */
+int event_base_loopbreak(struct event_base *);
+
+
+/**
+  Add a timer event.
+
+  @param ev the event struct
+  @param tv timeval struct
+ */
 #define evtimer_add(ev, tv)		event_add(ev, tv)
+
+
+/**
+  Define a timer event.
+
+  @param ev event struct to be modified
+  @param cb callback function
+  @param arg argument that will be passed to the callback function
+ */
 #define evtimer_set(ev, cb, arg)	event_set(ev, -1, 0, cb, arg)
+
+
+/**
+ * Delete a timer event.
+ *
+ * @param ev the event struct to be disabled
+ */
 #define evtimer_del(ev)			event_del(ev)
 #define evtimer_pending(ev, tv)		event_pending(ev, EV_TIMEOUT, tv)
 #define evtimer_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
 
+/**
+ * Add a timeout event.
+ *
+ * @param ev the event struct to be disabled
+ * @param tv the timeout value, in seconds
+ */
 #define timeout_add(ev, tv)		event_add(ev, tv)
+
+
+/**
+ * Define a timeout event.
+ *
+ * @param ev the event struct to be defined
+ * @param cb the callback to be invoked when the timeout expires
+ * @param arg the argument to be passed to the callback
+ */
 #define timeout_set(ev, cb, arg)	event_set(ev, -1, 0, cb, arg)
+
+
+/**
+ * Disable a timeout event.
+ *
+ * @param ev the timeout event to be disabled
+ */
 #define timeout_del(ev)			event_del(ev)
+
 #define timeout_pending(ev, tv)		event_pending(ev, EV_TIMEOUT, tv)
 #define timeout_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
 
@@ -182,32 +513,207 @@
 #define signal_pending(ev, tv)		event_pending(ev, EV_SIGNAL, tv)
 #define signal_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
 
+/**
+  Prepare an event structure to be added.
+
+  The function event_set() prepares the event structure ev to be used in
+  future calls to event_add() and event_del().  The event will be prepared to
+  call the function specified by the fn argument with an int argument
+  indicating the file descriptor, a short argument indicating the type of
+  event, and a void * argument given in the arg argument.  The fd indicates
+  the file descriptor that should be monitored for events.  The events can be
+  either EV_READ, EV_WRITE, or both.  Indicating that an application can read
+  or write from the file descriptor respectively without blocking.
+
+  The function fn will be called with the file descriptor that triggered the
+  event and the type of event which will be either EV_TIMEOUT, EV_SIGNAL,
+  EV_READ, or EV_WRITE.  The additional flag EV_PERSIST makes an event_add()
+  persistent until event_del() has been called.
+
+  @param ev an event struct to be modified
+  @param fd the file descriptor to be monitored
+  @param event desired events to monitor; can be EV_READ and/or EV_WRITE
+  @param fn callback function to be invoked when the event occurs
+  @param arg an argument to be passed to the callback function
+
+  @see event_add(), event_del(), event_once()
+
+ */
 void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
+
+/**
+  Schedule a one-time event to occur.
+
+  The function event_once() is similar to event_set().  However, it schedules
+  a callback to be called exactly once and does not require the caller to
+  prepare an event structure.
+
+  @param fd a file descriptor to monitor
+  @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ |
+         EV_WRITE
+  @param callback callback function to be invoked when the event occurs
+  @param arg an argument to be passed to the callback function
+  @param timeout the maximum amount of time to wait for the event, or NULL
+         to wait forever
+  @return 0 if successful, or -1 if an error occurred
+  @see event_set()
+
+ */
 int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
+
+
+/**
+  Schedule a one-time event (threadsafe variant)
+
+  The function event_base_once() is similar to event_set().  However, it
+  schedules a callback to be called exactly once and does not require the
+  caller to prepare an event structure.
+
+  @param base an event_base returned by event_init()
+  @param fd a file descriptor to monitor
+  @param events event(s) to monitor; can be any of EV_TIMEOUT | EV_READ |
+         EV_WRITE
+  @param callback callback function to be invoked when the event occurs
+  @param arg an argument to be passed to the callback function
+  @param timeout the maximum amount of time to wait for the event, or NULL
+         to wait forever
+  @return 0 if successful, or -1 if an error occurred
+  @see event_once()
+ */
 int event_base_once(struct event_base *, int, short, void (*)(int, short, void *), void
*, struct timeval *);
 
+
+/**
+  Add an event to the set of monitored events.
+
+  The function event_add() schedules the execution of the ev event when the
+  event specified in event_set() occurs or in at least the time specified in
+  the tv.  If tv is NULL, no timeout occurs and the function will only be
+  called if a matching event occurs on the file descriptor.  The event in the
+  ev argument must be already initialized by event_set() and may not be used
+  in calls to event_set() until it has timed out or been removed with
+  event_del().  If the event in the ev argument already has a scheduled
+  timeout, the old timeout will be replaced by the new one.
+
+  @param ev an event struct initialized via event_set()
+  @param timeout the maximum amount of time to wait for the event, or NULL
+         to wait forever
+  @return 0 if successful, or -1 if an error occurred
+  @see event_del(), event_set()
+  */
 int event_add(struct event *, struct timeval *);
+
+
+/**
+  Remove an event from the set of monitored events.
+
+  The function event_del() will cancel the event in the argument ev.  If the
+  event has already executed or has never been added the call will have no
+  effect.
+
+  @param ev an event struct to be removed from the working set
+  @return 0 if successful, or -1 if an error occurred
+  @see event_add()
+ */
 int event_del(struct event *);
+
 void event_active(struct event *, int, short);
 
+
+/**
+  Checks if a specific event is pending or scheduled.
+
+  @param ev an event struct previously passed to event_add()
+  @param event the requested event type; any of EV_TIMEOUT|EV_READ|
+         EV_WRITE|EV_SIGNAL
+  @param tv an alternate timeout (FIXME - is this true?)
+
+  @return 1 if the event is pending, or 0 if the event has not occurred
+
+ */
 int event_pending(struct event *, short, struct timeval *);
 
+
+/**
+  Test if an event structure has been initialized.
+
+  The event_initialized() macro can be used to check if an event has been
+  initialized.
+
+  @param ev an event structure to be tested
+  @return 1 if the structure has been initialized, or 0 if it has not been
+          initialized
+ */
 #ifdef WIN32
 #define event_initialized(ev)		((ev)->ev_flags & EVLIST_INIT &&
(ev)->ev_fd != (int)INVALID_HANDLE_VALUE)
 #else
 #define event_initialized(ev)		((ev)->ev_flags & EVLIST_INIT)
 #endif
 
-/* Some simple debugging functions */
+
+/**
+  Get the libevent version number.
+
+  @return a string containing the version number of libevent
+ */
 const char *event_get_version(void);
+
+
+/**
+  Get the kernel event notification mechanism used by libevent.
+
+  @return a string identifying the kernel event mechanism (kqueue, epoll, etc.)
+ */
 const char *event_get_method(void);
 
-/* These functions deal with event priorities */
-
+
+/**
+  Set the number of different event priorities.
+
+  By default libevent schedules all active events with the same priority.
+  However, some time it is desirable to process some events with a higher
+  priority than others.  For that reason, libevent supports strict priority
+  queues.  Active events with a lower priority are always processed before
+  events with a higher priority.
+
+  The number of different priorities can be set initially with the
+  event_priority_init() function.  This function should be called before the
+  first call to event_dispatch().  The event_priority_set() function can be
+  used to assign a priority to an event.  By default, libevent assigns the
+  middle priority to all events unless their priority is explicitly set.
+
+  @param npriorities the maximum number of priorities
+  @return 0 if successful, or -1 if an error occurred
+  @see event_base_priority_init(), event_priority_set()
+
+ */
 int	event_priority_init(int);
+
+
+/**
+  Set the number of different event priorities (threadsafe variant).
+
+  See the description of event_priority_init() for more information.
+
+  @param eb the event_base structure returned by event_init()
+  @param npriorities the maximum number of priorities
+  @return 0 if successful, or -1 if an error occurred
+  @see event_priority_init(), event_priority_set()
+ */
 int	event_base_priority_init(struct event_base *, int);
+
+
+/**
+  Assign a priority to an event.
+
+  @param ev an event struct
+  @param priority the new priority to be assigned
+  @return 0 if successful, or -1 if an error occurred
+  @see event_priority_init()
+  */
 int	event_priority_set(struct event *, int);
 
+
 /* These functions deal with buffering input and output */
 
 struct evbuffer {
@@ -259,40 +765,311 @@
 	short enabled;	/* events that are currently enabled */
 };
 
+
+/**
+  Create a new bufferevent.
+
+  libevent provides an abstraction on top of the regular event callbacks.
+  This abstraction is called a buffered event.  A buffered event provides
+  input and output buffers that get filled and drained automatically.  The
+  user of a buffered event no longer deals directly with the I/O, but
+  instead is reading from input and writing to output buffers.
+
+  Once initialized, the bufferevent structure can be used repeatedly with
+  bufferevent_enable() and bufferevent_disable().
+
+  When read enabled the bufferevent will try to read from the file descriptor
+  and call the read callback.  The write callback is executed whenever the
+  output buffer is drained below the write low watermark, which is 0 by
+  default.
+
+  If multiple bases are in use, bufferevent_base_set() must be called before
+  enabling the bufferevent for the first time.
+
+  @param fd the file descriptor from which data is read and written to.
+  		This file descriptor is not allowed to be a pipe(2).
+  @param readcb callback to invoke when there is data to be read, or NULL if
+         no callback is desired
+  @param writecb callback to invoke when the file descriptor is ready for
+         writing, or NULL if no callback is desired
+  @param errorcb callback to invoke when there is an error on the file
+         descriptor
+  @param cbarg an argument that will be supplied to each of the callbacks
+         (readcb, writecb, and errorcb)
+  @return a pointer to a newly allocated bufferevent struct, or NULL if an
+          error occurred
+  @see bufferevent_base_set(), bufferevent_free()
+  */
 struct bufferevent *bufferevent_new(int fd,
     evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
+
+
+/**
+  Assign a bufferevent to a specific event_base.
+
+  @param base an event_base returned by event_init()
+  @param bufev a bufferevent struct returned by bufferevent_new()
+  @return 0 if successful, or -1 if an error occurred
+  @see bufferevent_new()
+ */
 int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev);
+
+
+/**
+  Assign a priority to a bufferevent.
+
+  @param bufev a bufferevent struct
+  @param pri the priority to be assigned
+  @return 0 if successful, or -1 if an error occurred
+  */
 int bufferevent_priority_set(struct bufferevent *bufev, int pri);
+
+
+/**
+  Deallocate the storage associated with a bufferevent structure.
+
+  @param bufev the bufferevent structure to be freed.
+  */
 void bufferevent_free(struct bufferevent *bufev);
-int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
+
+
+/**
+  Write data to a bufferevent buffer.
+
+  The bufferevent_write() function can be used to write data to the file
+  descriptor.  The data is appended to the output buffer and written to the
+  descriptor automatically as it becomes available for writing.
+
+  @param bufev the bufferevent to be written to
+  @param data a pointer to the data to be written
+  @param size the length of the data, in bytes
+  @return 0 if successful, or -1 if an error occurred
+  @see bufferevent_write_buffer()
+  */
+int bufferevent_write(struct bufferevent *bufev,
+    const void *data, size_t size);
+
+
+/**
+  Write data from an evbuffer to a bufferevent buffer.  The evbuffer is
+  being drained as a result.
+
+  @param bufev the bufferevent to be written to
+  @param buf the evbuffer to be written
+  @return 0 if successful, or -1 if an error occurred
+  @see bufferevent_write()
+ */
 int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
+
+
+/**
+  Read data from a bufferevent buffer.
+
+  The bufferevent_read() function is used to read data from the input buffer.
+
+  @param bufev the bufferevent to be read from
+  @param data pointer to a buffer that will store the data
+  @param size the size of the data buffer, in bytes
+  @return the amount of data read, in bytes.
+ */
 size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
+
+/**
+  Enable a bufferevent.
+
+  @param bufev the bufferevent to be enabled
+  @param event any combination of EV_READ | EV_WRITE.
+  @return 0 if successful, or -1 if an error occurred
+  @see bufferevent_disable()
+ */
 int bufferevent_enable(struct bufferevent *bufev, short event);
+
+
+/**
+  Disable a bufferevent.
+
+  @param bufev the bufferevent to be disabled
+  @param event any combination of EV_READ | EV_WRITE.
+  @return 0 if successful, or -1 if an error occurred
+  @see bufferevent_enable()
+ */
 int bufferevent_disable(struct bufferevent *bufev, short event);
+
+
+/**
+  Set the read and write timeout for a buffered event.
+
+  @param bufev the bufferevent to be modified
+  @param timeout_read the read timeout
+  @param timeout_write the write timeout
+ */
 void bufferevent_settimeout(struct bufferevent *bufev,
     int timeout_read, int timeout_write);
 
+
 #define EVBUFFER_LENGTH(x)	(x)->off
 #define EVBUFFER_DATA(x)	(x)->buffer
 #define EVBUFFER_INPUT(x)	(x)->input
 #define EVBUFFER_OUTPUT(x)	(x)->output
 
+
+/**
+  Allocate storage for a new evbuffer.
+
+  @return a pointer to a newly allocated evbuffer struct, or NULL if an error
+          occurred
+ */
 struct evbuffer *evbuffer_new(void);
+
+
+/**
+  Deallocate storage for an evbuffer.
+
+  @param pointer to the evbuffer to be freed
+ */
 void evbuffer_free(struct evbuffer *);
+
+
+/**
+  Expands the available space in an event buffer.
+
+  Expands the available space in the event buffer to at least datlen
+
+  @param buf the event buffer to be expanded
+  @param datlen the new minimum length requirement
+  @return 0 if successful, or -1 if an error occurred
+*/
 int evbuffer_expand(struct evbuffer *, size_t);
+
+
+/**
+  Append data to the end of an evbuffer.
+
+  @param buf the event buffer to be appended to
+  @param data pointer to the beginning of the data buffer
+  @param datlen the number of bytes to be copied from the data buffer
+ */
 int evbuffer_add(struct evbuffer *, const void *, size_t);
+
+
+
+/**
+  Read data from an event buffer and drain the bytes read.
+
+  @param buf the event buffer to be read from
+  @param data the destination buffer to store the result
+  @param datlen the maximum size of the destination buffer
+  @return the number of bytes read
+ */
 int evbuffer_remove(struct evbuffer *, void *, size_t);
+
+
+/**
+ * Read a single line from an event buffer.
+ *
+ * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
+ * The returned buffer needs to be freed by the caller.
+ *
+ * @param buffer the evbuffer to read from
+ * @return pointer to a single line, or NULL if an error occurred
+ */
 char *evbuffer_readline(struct evbuffer *);
+
+
+/**
+  Move data from one evbuffer into another evbuffer.
+
+  This is a destructive add.  The data from one buffer moves into
+  the other buffer. The destination buffer is expanded as needed.
+
+  @param outbuf the output buffer
+  @param inbuf the input buffer
+  @return 0 if successful, or -1 if an error occurred
+ */
 int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
-int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...);
+
+
+/**
+  Append a formatted string to the end of an evbuffer.
+
+  @param buf the evbuffer that will be appended to
+  @param fmt a format string
+  @param ... arguments that will be passed to printf(3)
+  @return 0 if successful, or -1 if an error occurred
+ */
+int evbuffer_add_printf(struct evbuffer *, const char *fmt, ...)
+#ifdef __GNUC__
+  __attribute__((format(printf, 2, 3)))
+#endif
+;
+
+
+/**
+  Append a va_list formatted string to the end of an evbuffer.
+
+  @param buf the evbuffer that will be appended to
+  @param fmt a format string
+  @param ap a varargs va_list argument array that will be passed to vprintf(3)
+  @return 0 if successful, or -1 if an error occurred
+ */
 int evbuffer_add_vprintf(struct evbuffer *, const char *fmt, va_list ap);
+
+
+/**
+  Remove a specified number of bytes data from the beginning of an evbuffer.
+
+  @param buf the evbuffer to be drained
+  @param len the number of bytes to drain from the beginning of the buffer
+  @return 0 if successful, or -1 if an error occurred
+ */
 void evbuffer_drain(struct evbuffer *, size_t);
+
+
+/**
+  Write the contents of an evbuffer to a file descriptor.
+
+  The evbuffer will be drained after the bytes have been successfully written.
+
+  @param buffer the evbuffer to be written and drained
+  @param fd the file descriptor to be written to
+  @return the number of bytes written, or -1 if an error occurred
+  @see evbuffer_read()
+ */
 int evbuffer_write(struct evbuffer *, int);
+
+
+/**
+  Read from a file descriptor and store the result in an evbuffer.
+
+  @param buf the evbuffer to store the result
+  @param fd the file descriptor to read from
+  @param howmuch the number of bytes to be read
+  @return the number of bytes read, or -1 if an error occurred
+  @see evbuffer_write()
+ */
 int evbuffer_read(struct evbuffer *, int, int);
+
+
+/**
+  Find a string within an evbuffer.
+
+  @param buffer the evbuffer to be searched
+  @param what the string to be searched for
+  @param len the length of the search string
+  @return a pointer to the beginning of the search string, or NULL if the search failed.
+ */
 u_char *evbuffer_find(struct evbuffer *, const u_char *, size_t);
+
+/**
+  Set a callback to invoke when the evbuffer is modified.
+
+  @param buffer the evbuffer to be monitored
+  @param cb the callback function to invoke when the evbuffer is modified
+  @param cbarg an argument to be provided to the callback function
+ */
 void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void
*), void *);
 
-/* 
+/*
  * Marshaling tagged data - We assume that all tags are inserted in their
  * numeric order - so that unknown tags will always be higher than the
  * known ones - and we can just ignore the end of an event buffer.
@@ -300,37 +1077,47 @@
 
 void evtag_init(void);
 
-void evtag_marshal(struct evbuffer *evbuf, uint8_t tag, const void *data,
-    uint32_t len);
-
-void encode_int(struct evbuffer *evbuf, uint32_t number);
-
-void evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer);
-
-void evtag_marshal_string(struct evbuffer *buf, uint8_t tag,
+void evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag, const void *data,
+    ev_uint32_t len);
+
+/**
+  Encode an integer and store it in an evbuffer.
+
+  We encode integer's by nibbles; the first nibble contains the number
+  of significant nibbles - 1;  this allows us to encode up to 64-bit
+  integers.  This function is byte-order independent.
+
+  @param evbuf evbuffer to store the encoded number
+  @param number a 32-bit integer
+ */
+void encode_int(struct evbuffer *evbuf, ev_uint32_t number);
+
+void evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag,
+    ev_uint32_t integer);
+
+void evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag,
     const char *string);
 
-void evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag,
+void evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag,
     struct timeval *tv);
 
-void evtag_test(void);
-
-int evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst);
-int evtag_peek(struct evbuffer *evbuf, uint8_t *ptag);
-int evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength);
-int evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength);
+int evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag,
+    struct evbuffer *dst);
+int evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag);
+int evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength);
+int evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength);
 int evtag_consume(struct evbuffer *evbuf);
 
-int evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag,
-    uint32_t *pinteger);
-
-int evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data,
-    size_t len);
-
-int evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag,
+int evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag,
+    ev_uint32_t *pinteger);
+
+int evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag,
+    void *data, size_t len);
+
+int evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag,
     char **pstring);
 
-int evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag,
+int evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag,
     struct timeval *ptv);
 
 #ifdef __cplusplus

=== modified file 'extra/libevent/event_tagging.c'
--- a/extra/libevent/event_tagging.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/event_tagging.c	2008-04-28 17:52:20 +0000
@@ -25,22 +25,26 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
 #endif
 
 #ifdef WIN32
 #define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
 #include <windows.h>
 #undef WIN32_LEAN_AND_MEAN
 #else
 #include <sys/ioctl.h>
 #endif
 
-#include <sys/tree.h>
 #include <sys/queue.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -53,17 +57,22 @@
 #ifndef WIN32
 #include <syslog.h>
 #endif
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 
 #include "event.h"
+#include "evutil.h"
 #include "log.h"
 
-int decode_int(uint32_t *pnumber, struct evbuffer *evbuf);
+int evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf);
+int evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag);
+int evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf);
 
 static struct evbuffer *_buf;	/* not thread safe */
 
 void
-evtag_init()
+evtag_init(void)
 {
 	if (_buf != NULL)
 		return;
@@ -79,10 +88,10 @@
  */
 
 void
-encode_int(struct evbuffer *evbuf, uint32_t number)
+encode_int(struct evbuffer *evbuf, ev_uint32_t number)
 {
 	int off = 1, nibbles = 0;
-	uint8_t data[5];
+	ev_uint8_t data[5];
 
 	memset(data, 0, sizeof(data));
 	while (number) {
@@ -105,40 +114,105 @@
 }
 
 /*
+ * Support variable length encoding of tags; we use the high bit in each
+ * octet as a continuation signal.
+ */
+
+int
+evtag_encode_tag(struct evbuffer *evbuf, ev_uint32_t tag)
+{
+	int bytes = 0;
+	ev_uint8_t data[5];
+
+	memset(data, 0, sizeof(data));
+	do {
+		ev_uint8_t lower = tag & 0x7f;
+		tag >>= 7;
+
+		if (tag)
+			lower |= 0x80;
+
+		data[bytes++] = lower;
+	} while (tag);
+
+	if (evbuf != NULL)
+		evbuffer_add(evbuf, data, bytes);
+
+	return (bytes);
+}
+
+static int
+decode_tag_internal(ev_uint32_t *ptag, struct evbuffer *evbuf, int dodrain)
+{
+	ev_uint32_t number = 0;
+	ev_uint8_t *data = EVBUFFER_DATA(evbuf);
+	int len = EVBUFFER_LENGTH(evbuf);
+	int count = 0, shift = 0, done = 0;
+
+	while (count++ < len) {
+		ev_uint8_t lower = *data++;
+		number |= (lower & 0x7f) << shift;
+		shift += 7;
+
+		if (!(lower & 0x80)) {
+			done = 1;
+			break;
+		}
+	}
+
+	if (!done)
+		return (-1);
+
+	if (dodrain)
+		evbuffer_drain(evbuf, count);
+
+	if (ptag != NULL)
+		*ptag = number;
+
+	return (count);
+}
+
+int
+evtag_decode_tag(ev_uint32_t *ptag, struct evbuffer *evbuf)
+{
+	return (decode_tag_internal(ptag, evbuf, 1 /* dodrain */));
+}
+
+/*
  * Marshal a data type, the general format is as follows:
  *
  * tag number: one byte; length: var bytes; payload: var bytes
  */
 
 void
-evtag_marshal(struct evbuffer *evbuf, uint8_t tag,
-    const void *data, uint32_t len)
+evtag_marshal(struct evbuffer *evbuf, ev_uint32_t tag,
+    const void *data, ev_uint32_t len)
 {
-	evbuffer_add(evbuf, &tag, sizeof(tag));
+	evtag_encode_tag(evbuf, tag);
 	encode_int(evbuf, len);
 	evbuffer_add(evbuf, (void *)data, len);
 }
 
 /* Marshaling for integers */
 void
-evtag_marshal_int(struct evbuffer *evbuf, uint8_t tag, uint32_t integer)
+evtag_marshal_int(struct evbuffer *evbuf, ev_uint32_t tag, ev_uint32_t integer)
 {
 	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
 	encode_int(_buf, integer);
 
-	evbuffer_add(evbuf, &tag, sizeof(tag));
+	evtag_encode_tag(evbuf, tag);
 	encode_int(evbuf, EVBUFFER_LENGTH(_buf));
 	evbuffer_add_buffer(evbuf, _buf);
 }
 
 void
-evtag_marshal_string(struct evbuffer *buf, uint8_t tag, const char *string)
+evtag_marshal_string(struct evbuffer *buf, ev_uint32_t tag, const char *string)
 {
 	evtag_marshal(buf, tag, string, strlen(string));
 }
 
 void
-evtag_marshal_timeval(struct evbuffer *evbuf, uint8_t tag, struct timeval *tv)
+evtag_marshal_timeval(struct evbuffer *evbuf, ev_uint32_t tag, struct timeval *tv)
 {
 	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
 
@@ -150,31 +224,30 @@
 }
 
 static int
-decode_int_internal(uint32_t *pnumber, struct evbuffer *evbuf, int dodrain)
+decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain)
 {
-	uint32_t number = 0;
-	uint8_t *data = EVBUFFER_DATA(evbuf);
+	ev_uint32_t number = 0;
+	ev_uint8_t *data = EVBUFFER_DATA(evbuf);
 	int len = EVBUFFER_LENGTH(evbuf);
-	int nibbles = 0, off;
+	int nibbles = 0;
 
 	if (!len)
 		return (-1);
 
 	nibbles = ((data[0] & 0xf0) >> 4) + 1;
-	if (nibbles > 8 || (nibbles >> 1) > len - 1)
+	if (nibbles > 8 || (nibbles >> 1) + 1 > len)
 		return (-1);
+	len = (nibbles >> 1) + 1;
 
-	off = nibbles;
-	while (off > 0) {
+	while (nibbles > 0) {
 		number <<= 4;
-		if (off & 0x1)
-			number |= data[off >> 1] & 0x0f;
+		if (nibbles & 0x1)
+			number |= data[nibbles >> 1] & 0x0f;
 		else
-			number |= (data[off >> 1] & 0xf0) >> 4;
-		off--;
+			number |= (data[nibbles >> 1] & 0xf0) >> 4;
+		nibbles--;
 	}
 
-	len = (nibbles >> 1) + 1;
 	if (dodrain)
 		evbuffer_drain(evbuf, len);
 
@@ -184,55 +257,53 @@
 }
 
 int
-decode_int(uint32_t *pnumber, struct evbuffer *evbuf)
+evtag_decode_int(ev_uint32_t *pnumber, struct evbuffer *evbuf)
 {
 	return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0);
 }
 
 int
-evtag_peek(struct evbuffer *evbuf, uint8_t *ptag)
+evtag_peek(struct evbuffer *evbuf, ev_uint32_t *ptag)
 {
-	if (EVBUFFER_LENGTH(evbuf) < 2)
-		return (-1);
-	*ptag = EVBUFFER_DATA(evbuf)[0];
-
-	return (0);
+	return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */));
 }
 
 int
-evtag_peek_length(struct evbuffer *evbuf, uint32_t *plength)
+evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength)
 {
 	struct evbuffer tmp;
-	int res;
+	int res, len;
 
-	if (EVBUFFER_LENGTH(evbuf) < 2)
+	len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
+	if (len == -1)
 		return (-1);
 
 	tmp = *evbuf;
-	tmp.buffer += 1;
-	tmp.off -= 1;
+	tmp.buffer += len;
+	tmp.off -= len;
 
 	res = decode_int_internal(plength, &tmp, 0);
 	if (res == -1)
 		return (-1);
 
-	*plength += res + 1;
+	*plength += res + len;
 
 	return (0);
 }
 
 int
-evtag_payload_length(struct evbuffer *evbuf, uint32_t *plength)
+evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength)
 {
 	struct evbuffer tmp;
-	int res;
+	int res, len;
 
-	if (EVBUFFER_LENGTH(evbuf) < 2)
+	len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */);
+	if (len == -1)
 		return (-1);
 
 	tmp = *evbuf;
-	tmp.buffer += 1;
-	tmp.off -= 1;
+	tmp.buffer += len;
+	tmp.off -= len;
 
 	res = decode_int_internal(plength, &tmp, 0);
 	if (res == -1)
@@ -244,9 +315,10 @@
 int
 evtag_consume(struct evbuffer *evbuf)
 {
-	uint32_t len;
-	evbuffer_drain(evbuf, 1);
-	if (decode_int(&len, evbuf) == -1)
+	ev_uint32_t len;
+	if (decode_tag_internal(NULL, evbuf, 1 /* dodrain */) == -1)
+		return (-1);
+	if (evtag_decode_int(&len, evbuf) == -1)
 		return (-1);
 	evbuffer_drain(evbuf, len);
 
@@ -256,15 +328,14 @@
 /* Reads the data type from an event buffer */
 
 int
-evtag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst)
+evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst)
 {
-	uint8_t tag;
-	uint32_t len;
-	uint32_t integer;
+	ev_uint32_t len;
+	ev_uint32_t integer;
 
-	if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag))
+	if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1)
 		return (-1);
-	if (decode_int(&integer, src) == -1)
+	if (evtag_decode_int(&integer, src) == -1)
 		return (-1);
 	len = integer;
 
@@ -276,24 +347,24 @@
 
 	evbuffer_drain(src, len);
 
-	*ptag = tag;
 	return (len);
 }
 
 /* Marshaling for integers */
 
 int
-evtag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag,
-    uint32_t *pinteger)
+evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag,
+    ev_uint32_t *pinteger)
 {
-	uint8_t tag;
-	uint32_t len;
-	uint32_t integer;
+	ev_uint32_t tag;
+	ev_uint32_t len;
+	ev_uint32_t integer;
 
-	if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) ||
-	    tag != need_tag)
-		return (-1);
-	if (decode_int(&integer, evbuf) == -1)
+	if (decode_tag_internal(&tag, evbuf, 1 /* dodrain */) == -1)
+		return (-1);
+	if (need_tag != tag)
+		return (-1);
+	if (evtag_decode_int(&integer, evbuf) == -1)
 		return (-1);
 	len = integer;
 
@@ -306,16 +377,16 @@
 
 	evbuffer_drain(evbuf, len);
 
-	return (decode_int(pinteger, _buf));
+	return (evtag_decode_int(pinteger, _buf));
 }
 
 /* Unmarshal a fixed length tag */
 
 int
-evtag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data,
+evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data,
     size_t len)
 {
-	uint8_t tag;
+	ev_uint32_t tag;
 
 	/* Initialize this event buffer so that we can read into it */
 	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
@@ -332,10 +403,10 @@
 }
 
 int
-evtag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag,
+evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag,
     char **pstring)
 {
-	uint8_t tag;
+	ev_uint32_t tag;
 
 	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
 
@@ -351,20 +422,20 @@
 }
 
 int
-evtag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag,
+evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag,
     struct timeval *ptv)
 {
-	uint8_t tag;
-	uint32_t integer;
+	ev_uint32_t tag;
+	ev_uint32_t integer;
 
 	evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf));
 	if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag)
 		return (-1);
 
-	if (decode_int(&integer, _buf) == -1)
+	if (evtag_decode_int(&integer, _buf) == -1)
 		return (-1);
 	ptv->tv_sec = integer;
-	if (decode_int(&integer, _buf) == -1)
+	if (evtag_decode_int(&integer, _buf) == -1)
 		return (-1);
 	ptv->tv_usec = integer;
 

=== modified file 'extra/libevent/evhttp.h'
--- a/extra/libevent/evhttp.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/evhttp.h	2008-04-28 17:52:20 +0000
@@ -40,7 +40,8 @@
 #undef WIN32_LEAN_AND_MEAN
 #endif
 
-/*
+/** @file evhttp.h
+ *
  * Basic support for HTTP serving.
  *
  * As libevent is a library for dealing with event notification and most
@@ -64,58 +65,124 @@
 struct evhttp_request;
 struct evkeyvalq;
 
-/* Start an HTTP server on the specified address and port */
-struct evhttp *evhttp_start(const char *address, u_short port);
-
-/*
- * Free the previously create HTTP server.  Works only if no requests are
- * currently being served.
+/** Create a new HTTP server
+ *
+ * @param base (optional) the event base to receive the HTTP events
+ * @return a pointer to a newly initialized evhttp server structure
+ */
+struct evhttp *evhttp_new(struct event_base *base);
+
+/**
+ * Binds an HTTP server on the specified address and port.
+ *
+ * Can be called multiple times to bind the same http server
+ * to multiple different ports.
+ *
+ * @param http a pointer to an evhttp object
+ * @param address a string containing the IP address to listen(2) on
+ * @param port the port number to listen on
+ * @return a newly allocated evhttp struct
+ * @see evhttp_free()
+ */
+int evhttp_bind_socket(struct evhttp *http, const char *address, u_short port);
+
+/**
+ * Free the previously created HTTP server.
+ *
+ * Works only if no requests are currently being served.
+ *
+ * @param http the evhttp server object to be freed
+ * @see evhttp_start()
  */
 void evhttp_free(struct evhttp* http);
 
-/* Set a callback for a specified URI */
+/** Set a callback for a specified URI */
 void evhttp_set_cb(struct evhttp *, const char *,
     void (*)(struct evhttp_request *, void *), void *);
 
-/* Removes the callback for a specified URI */
+/** Removes the callback for a specified URI */
 int evhttp_del_cb(struct evhttp *, const char *);
 
-/* Set a callback for all requests that are not caught by specific callbacks */
+/** Set a callback for all requests that are not caught by specific callbacks
+ */
 void evhttp_set_gencb(struct evhttp *,
     void (*)(struct evhttp_request *, void *), void *);
 
+/**
+ * Set the timeout for an HTTP request.
+ *
+ * @param http an evhttp object
+ * @param timeout_in_secs the timeout, in seconds
+ */
 void evhttp_set_timeout(struct evhttp *, int timeout_in_secs);
 
 /* Request/Response functionality */
 
-void evhttp_send_error(struct evhttp_request *, int, const char *);
-void evhttp_send_reply(struct evhttp_request *, int, const char *,
-    struct evbuffer *);
+/**
+ * Send an HTML error message to the client.
+ *
+ * @param req a request object
+ * @param error the HTTP error code
+ * @param reason a brief explanation of the error
+ */
+void evhttp_send_error(struct evhttp_request *req, int error,
+    const char *reason);
+
+/**
+ * Send an HTML reply to the client.
+ *
+ * @param req a request object
+ * @param code the HTTP response code to send
+ * @param reason a brief message to send with the response code
+ * @param databuf the body of the response
+ */
+void evhttp_send_reply(struct evhttp_request *req, int code,
+    const char *reason, struct evbuffer *databuf);
 
 /* Low-level response interface, for streaming/chunked replies */
 void evhttp_send_reply_start(struct evhttp_request *, int, const char *);
 void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *);
 void evhttp_send_reply_end(struct evhttp_request *);
-	
-/* Interfaces for making requests */
+
+/**
+ * Start an HTTP server on the specified address and port
+ *
+ * DEPRECATED: it does not allow an event base to be specified
+ *
+ * @param address the address to which the HTTP server should be bound
+ * @param port the port number on which the HTTP server should listen
+ * @return an struct evhttp object
+ */
+struct evhttp *evhttp_start(const char *address, u_short port);
+
+/*
+ * Interfaces for making requests
+ */
 enum evhttp_cmd_type { EVHTTP_REQ_GET, EVHTTP_REQ_POST, EVHTTP_REQ_HEAD };
 
 enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
 
-/* 
+/**
  * the request structure that a server receives.
  * WARNING: expect this structure to change.  I will try to provide
  * reasonable accessors.
  */
 struct evhttp_request {
+#if defined(TAILQ_ENTRY)
 	TAILQ_ENTRY(evhttp_request) next;
+#else
+struct {
+	struct evhttp_request *tqe_next;
+	struct evhttp_request **tqe_prev;
+}       next;
+#endif
 
 	/* the connection object that this request belongs to */
 	struct evhttp_connection *evcon;
 	int flags;
-#define EVHTTP_REQ_OWN_CONNECTION	0x0001	
+#define EVHTTP_REQ_OWN_CONNECTION	0x0001
 #define EVHTTP_PROXY_REQUEST		0x0002
-	
+
 	struct evkeyvalq *input_headers;
 	struct evkeyvalq *output_headers;
 
@@ -130,13 +197,13 @@
 
 	char major;			/* HTTP Major number */
 	char minor;			/* HTTP Minor number */
-	
+
 	int got_firstline;
 	int response_code;		/* HTTP Response code */
 	char *response_code_line;	/* Readable response */
 
 	struct evbuffer *input_buffer;	/* read data */
-	int ntoread;
+	ev_int64_t ntoread;
 	int chunked;
 
 	struct evbuffer *output_buffer;	/* outgoing post or data */
@@ -145,7 +212,7 @@
 	void (*cb)(struct evhttp_request *, void *);
 	void *cb_arg;
 
-	/* 
+	/*
 	 * Chunked data callback - call for each completed chunk if
 	 * specified.  If not specified, all the data is delivered via
 	 * the regular callback.
@@ -153,7 +220,7 @@
 	void (*chunk_cb)(struct evhttp_request *, void *);
 };
 
-/* 
+/**
  * Creates a new request object that needs to be filled in with the request
  * parameters.  The callback is executed when the request completed or an
  * error occurred.
@@ -161,14 +228,14 @@
 struct evhttp_request *evhttp_request_new(
 	void (*cb)(struct evhttp_request *, void *), void *arg);
 
-/* enable delivery of chunks to requestor */
+/** enable delivery of chunks to requestor */
 void evhttp_request_set_chunked_cb(struct evhttp_request *,
     void (*cb)(struct evhttp_request *, void *));
 
-/* Frees the request object and removes associated events. */
+/** Frees the request object and removes associated events. */
 void evhttp_request_free(struct evhttp_request *req);
 
-/*
+/**
  * A connection object that can be used to for making HTTP requests.  The
  * connection object tries to establish the connection when it is given an
  * http request object.
@@ -176,26 +243,37 @@
 struct evhttp_connection *evhttp_connection_new(
 	const char *address, unsigned short port);
 
-/* Frees an http connection */
+/** Frees an http connection */
 void evhttp_connection_free(struct evhttp_connection *evcon);
 
-/* Sets the timeout for events related to this connection */
+/** sets the ip address from which http connections are made */
+void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
+    const char *address);
+
+/** Sets the timeout for events related to this connection */
 void evhttp_connection_set_timeout(struct evhttp_connection *evcon,
     int timeout_in_secs);
 
-/* Sets the retry limit for this connection - -1 repeats indefnitely */
+/** Sets the retry limit for this connection - -1 repeats indefnitely */
 void evhttp_connection_set_retries(struct evhttp_connection *evcon,
     int retry_max);
 
-/* Set a callback for connection close. */
+/** Set a callback for connection close. */
 void evhttp_connection_set_closecb(struct evhttp_connection *evcon,
     void (*)(struct evhttp_connection *, void *), void *);
 
-/* Get the remote address and port associated with this connection. */
+/**
+ * Associates an event base with the connection - can only be called
+ * on a freshly created connection object that has not been used yet.
+ */
+void evhttp_connection_set_base(struct evhttp_connection *evcon,
+    struct event_base *base);
+
+/** Get the remote address and port associated with this connection. */
 void evhttp_connection_get_peer(struct evhttp_connection *evcon,
     char **address, u_short *port);
 
-/* The connection gets ownership of the request */
+/** The connection gets ownership of the request */
 int evhttp_make_request(struct evhttp_connection *evcon,
     struct evhttp_request *req,
     enum evhttp_cmd_type type, const char *uri);
@@ -210,10 +288,51 @@
 void evhttp_clear_headers(struct evkeyvalq *);
 
 /* Miscellaneous utility functions */
+
+
+/**
+  Helper function to encode a URI.
+
+  The returned string must be freed by the caller.
+
+  @param uri an unencoded URI
+  @return a newly allocated URI-encoded string
+ */
 char *evhttp_encode_uri(const char *uri);
+
+
+/**
+  Helper function to decode a URI.
+
+  The returned string must be freed by the caller.
+
+  @param uri an encoded URI
+  @return a newly allocated unencoded URI
+ */
 char *evhttp_decode_uri(const char *uri);
+
+
+/**
+ * Helper function to parse out arguments in a query.
+ * The arguments are separated by key and value.
+ * URI should already be decoded.
+ */
 void evhttp_parse_query(const char *uri, struct evkeyvalq *);
+
+
+/**
+ * Escape HTML character entities in a string.
+ *
+ * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
+ * &#039; and &amp; correspondingly.
+ *
+ * The returned string needs to be freed by the caller.
+ *
+ * @param html an unescaped HTML string
+ * @return an escaped HTML string
+ */
 char *evhttp_htmlescape(const char *html);
+
 #ifdef __cplusplus
 }
 #endif

=== modified file 'extra/libevent/evport.c'
--- a/extra/libevent/evport.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/evport.c	2008-04-28 17:52:20 +0000
@@ -12,20 +12,20 @@
  * 3. The name of the author may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY SUN MICROSYSTEMS, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
 /*
- * Copyright (c) 2006 Sun Microsystems. All rights reserved.
+ * Copyright (c) 2007 Sun Microsystems. All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -47,8 +47,7 @@
  * necessary when large fd's come in. reassociate() takes care of maintaining
  * the proper file-descriptor/event-port associations.
  *
- * As in the select(2) implementation, signals are handled by evsignal, and
- * evport_recalc does almost nothing.
+ * As in the select(2) implementation, signals are handled by evsignal.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -57,14 +56,9 @@
 
 #ifdef HAVE_EVENT_PORTS
 
-#ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
-#else
-#include <sys/_time.h>
-#endif
 #include <assert.h>
 #include <sys/queue.h>
-#include <sys/tree.h>
 #include <errno.h>
 #include <poll.h>
 #include <port.h>
@@ -120,13 +114,12 @@
 	int		ed_nevents;	/* number of allocated fdi's 	 */
 	struct fd_info *ed_fds;		/* allocated fdi table 		 */
 	/* fdi's that we need to reassoc */
-	struct fd_info *ed_pending[EVENTS_PER_GETN];
+	int ed_pending[EVENTS_PER_GETN]; /* fd's with pending events */
 };
 
 static void*	evport_init	(struct event_base *);
 static int 	evport_add	(void *, struct event *);
 static int 	evport_del	(void *, struct event *);
-static int 	evport_recalc	(struct event_base *, void *, int);
 static int 	evport_dispatch	(struct event_base *, void *, struct timeval *);
 static void	evport_dealloc	(struct event_base *, void *);
 
@@ -135,9 +128,9 @@
 	evport_init,
 	evport_add,
 	evport_del,
-	evport_recalc,
 	evport_dispatch,
-	evport_dealloc
+	evport_dealloc,
+	1 /* need reinit */
 };
 
 /*
@@ -148,6 +141,7 @@
 evport_init(struct event_base *base)
 {
 	struct evport_data *evpd;
+	int i;
 	/*
 	 * Disable event ports when this environment variable is set 
 	 */
@@ -172,7 +166,8 @@
 		return (NULL);
 	}
 	evpd->ed_nevents = DEFAULT_NFDS;
-	memset(&evpd->ed_pending, 0, EVENTS_PER_GETN * sizeof(struct fd_info*));
+	for (i = 0; i < EVENTS_PER_GETN; i++)
+		evpd->ed_pending[i] = -1;
 
 	evsignal_init(base);
 
@@ -242,10 +237,8 @@
 grow(struct evport_data *epdp, int factor)
 {
 	struct fd_info *tmp;
-	struct fd_info *old = epdp->ed_fds;
 	int oldsize = epdp->ed_nevents;
 	int newsize = factor * oldsize;
-	int ii;
 	assert(factor > 1);
 
 	check_evportop(epdp);
@@ -256,15 +249,6 @@
 	epdp->ed_fds = tmp;
 	memset((char*) (epdp->ed_fds + oldsize), 0, 
 	    (newsize - oldsize)*sizeof(struct fd_info));
-
-	/* The ev_pending array contains pointers into the released array. */
-	for (ii = 0; ii < EVENTS_PER_GETN; ++ii) {
-		if (epdp->ed_pending[ii] != 0) {
-			int offset = epdp->ed_pending[ii] - old;
-			epdp->ed_pending[ii] = epdp->ed_fds + offset;
-		}
-	}
-        
 	epdp->ed_nevents = newsize;
 
 	check_evportop(epdp);
@@ -283,14 +267,9 @@
 	int sysevents = FDI_TO_SYSEVENTS(fdip);
 
 	if (sysevents != 0) {
-		if ((-1 == port_associate(epdp->ed_port, PORT_SOURCE_FD,
-		    fd, sysevents, NULL))) {
-			perror("port_associate");
-			return (-1);
-		}
-	} else {
-		if (-1 == port_dissociate(epdp->ed_port, PORT_SOURCE_FD, fd)) {
-			perror("port_dissociate");
+		if (port_associate(epdp->ed_port, PORT_SOURCE_FD,
+				   fd, sysevents, NULL) == -1) {
+			event_warn("port_associate");
 			return (-1);
 		}
 	}
@@ -339,26 +318,29 @@
 	 * loop below.
 	 */
 	for (i = 0; i < EVENTS_PER_GETN; ++i) {
-		struct fd_info *fdi = epdp->ed_pending[i];
+		struct fd_info *fdi = NULL;
+		if (epdp->ed_pending[i] != -1) {
+			fdi = &(epdp->ed_fds[epdp->ed_pending[i]]);
+		}
 
 		if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
 			int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd : 
 			    fdi->fdi_wevt->ev_fd;
 			reassociate(epdp, fdi, fd);
-			epdp->ed_pending[i] = NULL;
+			epdp->ed_pending[i] = -1;
 		}
 	}
 
 	if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, 
 		    (unsigned int *) &nevents, ts_p)) == -1) {
-		if (errno == EINTR) {
+		if (errno == EINTR || errno == EAGAIN) {
 			evsignal_process(base);
 			return (0);
 		} else if (errno == ETIME) {
 			if (nevents == 0)
 				return (0);
 		} else {
-			perror("port_getn");
+			event_warn("port_getn");
 			return (-1);
 		}
 	} else if (base->sig.evsignal_caught) {
@@ -375,6 +357,7 @@
 
 		check_evportop(epdp);
 		check_event(pevt);
+		epdp->ed_pending[i] = fd;
 
 		/*
 		 * Figure out what kind of event it was 
@@ -390,36 +373,18 @@
 		fdi = &(epdp->ed_fds[fd]);
 
 		/*
-		 * We now check for each of the possible events (READ or WRITE).
-		 * If the event is not persistent, then we delete it. Then, we
-		 * activate the event (which will cause its callback to be
-		 * executed).
+		 * We now check for each of the possible events (READ
+		 * or WRITE).  Then, we activate the event (which will
+		 * cause its callback to be executed).
 		 */
 
 		if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
-			if (!(ev->ev_events & EV_PERSIST))
-				event_del(ev);
 			event_active(ev, res, 1);
 		}
 
 		if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
-			if (!(ev->ev_events & EV_PERSIST))
-				event_del(ev);
 			event_active(ev, res, 1);
 		}
-
-		/*
-		 * If there are still events (they haven't been deleted), then
-		 * we must reassociate the port, since the event port interface
-		 * dissociates them automatically. 
-		 *
-		 * But we can't do it right away, because the event hasn't
-		 * handled this event yet, so of course there's still data
-		 * waiting!
-		 */
-		if(FDI_HAS_EVENTS(fdi)) {
-			epdp->ed_pending[i] = fdi;
-		}
 	} /* end of all events gotten */
 
 	check_evportop(epdp);
@@ -429,19 +394,6 @@
 
 
 /*
- * Copied from the version in select.c
- */
-
-static int
-evport_recalc(struct event_base *base, void *arg, int max)
-{
-	struct evport_data *evpd = arg;
-	check_evportop(evpd);
-	return (0);
-}
-
-
-/*
  * Adds the given event (so that you will be notified when it happens via
  * the callback function).
  */
@@ -493,6 +445,8 @@
 {
 	struct evport_data *evpd = arg;
 	struct fd_info *fdi;
+	int i;
+	int associated = 1;
 
 	check_evportop(evpd);
 
@@ -507,6 +461,12 @@
 		return (-1);
 	}
 
+	for (i = 0; i < EVENTS_PER_GETN; ++i) {
+		if (evpd->ed_pending[i] == ev->ev_fd) {
+			associated = 0;
+			break;
+		}
+	}
 
 	fdi = &evpd->ed_fds[ev->ev_fd];
 	if (ev->ev_events & EV_READ)
@@ -514,7 +474,29 @@
 	if (ev->ev_events & EV_WRITE)
 		fdi->fdi_wevt = NULL;
 
-	return reassociate(evpd, fdi, ev->ev_fd);
+	if (associated) {
+		if (!FDI_HAS_EVENTS(fdi) &&
+		    port_dissociate(evpd->ed_port, PORT_SOURCE_FD,
+		    ev->ev_fd) == -1) {	 
+			/*
+			 * Ignre EBADFD error the fd could have been closed
+			 * before event_del() was called.
+			 */
+			if (errno != EBADFD) {
+				event_warn("port_dissociate");
+				return (-1);
+			}
+		} else {
+			if (FDI_HAS_EVENTS(fdi)) {
+				return (reassociate(evpd, fdi, ev->ev_fd));
+			}
+		}
+	} else {
+		if (fdi->fdi_revt == NULL && fdi->fdi_wevt == NULL) {
+			evpd->ed_pending[i] = -1;
+		}
+	}
+	return 0;
 }
 
 

=== added file 'extra/libevent/evrpc-internal.h'
--- a/extra/libevent/evrpc-internal.h	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/evrpc-internal.h	2008-04-28 17:52:20 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _EVRPC_INTERNAL_H_
+#define _EVRPC_INTERNAL_H_
+
+#include "http-internal.h"
+
+struct evrpc;
+
+#define EVRPC_URI_PREFIX "/.rpc."
+
+struct evrpc_hook {
+	TAILQ_ENTRY(evrpc_hook) (next);
+
+	/* returns -1; if the rpc should be aborted, is allowed to rewrite */
+	int (*process)(struct evhttp_request *, struct evbuffer *, void *);
+	void *process_arg;
+};
+
+TAILQ_HEAD(evrpc_hook_list, evrpc_hook);
+
+/*
+ * this is shared between the base and the pool, so that we can reuse
+ * the hook adding functions; we alias both evrpc_pool and evrpc_base
+ * to this common structure.
+ */
+struct _evrpc_hooks {
+	/* hooks for processing outbound and inbound rpcs */
+	struct evrpc_hook_list in_hooks;
+	struct evrpc_hook_list out_hooks;
+};
+
+#define input_hooks common.in_hooks
+#define output_hooks common.out_hooks
+
+struct evrpc_base {
+	struct _evrpc_hooks common;
+
+	/* the HTTP server under which we register our RPC calls */
+	struct evhttp* http_server;
+
+	/* a list of all RPCs registered with us */
+	TAILQ_HEAD(evrpc_list, evrpc) registered_rpcs;
+};
+
+struct evrpc_req_generic;
+void evrpc_reqstate_free(struct evrpc_req_generic* rpc_state);
+
+/* A pool for holding evhttp_connection objects */
+struct evrpc_pool {
+	struct _evrpc_hooks common;
+
+	struct event_base *base;
+
+	struct evconq connections;
+
+	int timeout;
+
+	TAILQ_HEAD(evrpc_requestq, evrpc_request_wrapper) requests;
+};
+
+
+#endif /* _EVRPC_INTERNAL_H_ */

=== added file 'extra/libevent/evrpc.c'
--- a/extra/libevent/evrpc.c	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/evrpc.c	2008-04-28 17:52:20 +0000
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2000-2004 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#include "misc.h"
+#endif
+
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <assert.h>
+
+#include "event.h"
+#include "evrpc.h"
+#include "evrpc-internal.h"
+#include "evhttp.h"
+#include "evutil.h"
+#include "log.h"
+
+struct evrpc_base *
+evrpc_init(struct evhttp *http_server)
+{
+	struct evrpc_base* base = calloc(1, sizeof(struct evrpc_base));
+	if (base == NULL)
+		return (NULL);
+
+	/* we rely on the tagging sub system */
+	evtag_init();
+
+	TAILQ_INIT(&base->registered_rpcs);
+	TAILQ_INIT(&base->input_hooks);
+	TAILQ_INIT(&base->output_hooks);
+	base->http_server = http_server;
+
+	return (base);
+}
+
+void
+evrpc_free(struct evrpc_base *base)
+{
+	struct evrpc *rpc;
+	struct evrpc_hook *hook;
+
+	while ((rpc = TAILQ_FIRST(&base->registered_rpcs)) != NULL) {
+		assert(evrpc_unregister_rpc(base, rpc->uri));
+	}
+	while ((hook = TAILQ_FIRST(&base->input_hooks)) != NULL) {
+		assert(evrpc_remove_hook(base, INPUT, hook));
+	}
+	while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) {
+		assert(evrpc_remove_hook(base, OUTPUT, hook));
+	}
+	free(base);
+}
+
+void *
+evrpc_add_hook(void *vbase,
+    enum EVRPC_HOOK_TYPE hook_type,
+    int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
+    void *cb_arg)
+{
+	struct _evrpc_hooks *base = vbase;
+	struct evrpc_hook_list *head = NULL;
+	struct evrpc_hook *hook = NULL;
+	switch (hook_type) {
+	case INPUT:
+		head = &base->in_hooks;
+		break;
+	case OUTPUT:
+		head = &base->out_hooks;
+		break;
+	default:
+		assert(hook_type == INPUT || hook_type == OUTPUT);
+	}
+
+	hook = calloc(1, sizeof(struct evrpc_hook));
+	assert(hook != NULL);
+	
+	hook->process = cb;
+	hook->process_arg = cb_arg;
+	TAILQ_INSERT_TAIL(head, hook, next);
+
+	return (hook);
+}
+
+static int
+evrpc_remove_hook_internal(struct evrpc_hook_list *head, void *handle)
+{
+	struct evrpc_hook *hook = NULL;
+	TAILQ_FOREACH(hook, head, next) {
+		if (hook == handle) {
+			TAILQ_REMOVE(head, hook, next);
+			free(hook);
+			return (1);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * remove the hook specified by the handle
+ */
+
+int
+evrpc_remove_hook(void *vbase, enum EVRPC_HOOK_TYPE hook_type, void *handle)
+{
+	struct _evrpc_hooks *base = vbase;
+	struct evrpc_hook_list *head = NULL;
+	switch (hook_type) {
+	case INPUT:
+		head = &base->in_hooks;
+		break;
+	case OUTPUT:
+		head = &base->out_hooks;
+		break;
+	default:
+		assert(hook_type == INPUT || hook_type == OUTPUT);
+	}
+
+	return (evrpc_remove_hook_internal(head, handle));
+}
+
+static int
+evrpc_process_hooks(struct evrpc_hook_list *head,
+    struct evhttp_request *req, struct evbuffer *evbuf)
+{
+	struct evrpc_hook *hook;
+	TAILQ_FOREACH(hook, head, next) {
+		if (hook->process(req, evbuf, hook->process_arg) == -1)
+			return (-1);
+	}
+
+	return (0);
+}
+
+static void evrpc_pool_schedule(struct evrpc_pool *pool);
+static void evrpc_request_cb(struct evhttp_request *, void *);
+void evrpc_request_done(struct evrpc_req_generic*);
+
+/*
+ * Registers a new RPC with the HTTP server.   The evrpc object is expected
+ * to have been filled in via the EVRPC_REGISTER_OBJECT macro which in turn
+ * calls this function.
+ */
+
+static char *
+evrpc_construct_uri(const char *uri)
+{
+	char *constructed_uri;
+	int constructed_uri_len;
+
+	constructed_uri_len = strlen(EVRPC_URI_PREFIX) + strlen(uri) + 1;
+	if ((constructed_uri = malloc(constructed_uri_len)) == NULL)
+		event_err(1, "%s: failed to register rpc at %s",
+		    __func__, uri);
+	memcpy(constructed_uri, EVRPC_URI_PREFIX, strlen(EVRPC_URI_PREFIX));
+	memcpy(constructed_uri + strlen(EVRPC_URI_PREFIX), uri, strlen(uri));
+	constructed_uri[constructed_uri_len - 1] = '\0';
+
+	return (constructed_uri);
+}
+
+int
+evrpc_register_rpc(struct evrpc_base *base, struct evrpc *rpc,
+    void (*cb)(struct evrpc_req_generic *, void *), void *cb_arg)
+{
+	char *constructed_uri = evrpc_construct_uri(rpc->uri);
+
+	rpc->base = base;
+	rpc->cb = cb;
+	rpc->cb_arg = cb_arg;
+
+	TAILQ_INSERT_TAIL(&base->registered_rpcs, rpc, next);
+
+	evhttp_set_cb(base->http_server,
+	    constructed_uri,
+	    evrpc_request_cb,
+	    rpc);
+	
+	free(constructed_uri);
+
+	return (0);
+}
+
+int
+evrpc_unregister_rpc(struct evrpc_base *base, const char *name)
+{
+	char *registered_uri = NULL;
+	struct evrpc *rpc;
+
+	/* find the right rpc; linear search might be slow */
+	TAILQ_FOREACH(rpc, &base->registered_rpcs, next) {
+		if (strcmp(rpc->uri, name) == 0)
+			break;
+	}
+	if (rpc == NULL) {
+		/* We did not find an RPC with this name */
+		return (-1);
+	}
+	TAILQ_REMOVE(&base->registered_rpcs, rpc, next);
+	
+	free((char *)rpc->uri);
+	free(rpc);
+
+        registered_uri = evrpc_construct_uri(name);
+
+	/* remove the http server callback */
+	assert(evhttp_del_cb(base->http_server, registered_uri) == 0);
+
+	free(registered_uri);
+	return (0);
+}
+
+static void
+evrpc_request_cb(struct evhttp_request *req, void *arg)
+{
+	struct evrpc *rpc = arg;
+	struct evrpc_req_generic *rpc_state = NULL;
+
+	/* let's verify the outside parameters */
+	if (req->type != EVHTTP_REQ_POST ||
+	    EVBUFFER_LENGTH(req->input_buffer) <= 0)
+		goto error;
+
+	/*
+	 * we might want to allow hooks to suspend the processing,
+	 * but at the moment, we assume that they just act as simple
+	 * filters.
+	 */
+	if (evrpc_process_hooks(&rpc->base->input_hooks,
+		req, req->input_buffer) == -1)
+		goto error;
+
+	rpc_state = calloc(1, sizeof(struct evrpc_req_generic));
+	if (rpc_state == NULL)
+		goto error;
+
+	/* let's check that we can parse the request */
+	rpc_state->request = rpc->request_new();
+	if (rpc_state->request == NULL)
+		goto error;
+
+	rpc_state->rpc = rpc;
+
+	if (rpc->request_unmarshal(
+		    rpc_state->request, req->input_buffer) == -1) {
+		/* we failed to parse the request; that's a bummer */
+		goto error;
+	}
+
+	/* at this point, we have a well formed request, prepare the reply */
+
+	rpc_state->reply = rpc->reply_new();
+	if (rpc_state->reply == NULL)
+		goto error;
+
+	rpc_state->http_req = req;
+	rpc_state->done = evrpc_request_done;
+
+	/* give the rpc to the user; they can deal with it */
+	rpc->cb(rpc_state, rpc->cb_arg);
+
+	return;
+
+error:
+	evrpc_reqstate_free(rpc_state);
+	evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
+	return;
+}
+
+void
+evrpc_reqstate_free(struct evrpc_req_generic* rpc_state)
+{
+	/* clean up all memory */
+	if (rpc_state != NULL) {
+		struct evrpc *rpc = rpc_state->rpc;
+
+		if (rpc_state->request != NULL)
+			rpc->request_free(rpc_state->request);
+		if (rpc_state->reply != NULL)
+			rpc->reply_free(rpc_state->reply);
+		free(rpc_state);
+	}
+}
+
+void
+evrpc_request_done(struct evrpc_req_generic* rpc_state)
+{
+	struct evhttp_request *req = rpc_state->http_req;
+	struct evrpc *rpc = rpc_state->rpc;
+	struct evbuffer* data = NULL;
+
+	if (rpc->reply_complete(rpc_state->reply) == -1) {
+		/* the reply was not completely filled in.  error out */
+		goto error;
+	}
+
+	if ((data = evbuffer_new()) == NULL) {
+		/* out of memory */
+		goto error;
+	}
+
+	/* serialize the reply */
+	rpc->reply_marshal(data, rpc_state->reply);
+
+	/* do hook based tweaks to the request */
+	if (evrpc_process_hooks(&rpc->base->output_hooks,
+		req, data) == -1)
+		goto error;
+
+	/* on success, we are going to transmit marshaled binary data */
+	if (evhttp_find_header(req->output_headers, "Content-Type") == NULL) {
+		evhttp_add_header(req->output_headers,
+		    "Content-Type", "application/octet-stream");
+	}
+
+	evhttp_send_reply(req, HTTP_OK, "OK", data);
+
+	evbuffer_free(data);
+
+	evrpc_reqstate_free(rpc_state);
+
+	return;
+
+error:
+	if (data != NULL)
+		evbuffer_free(data);
+	evrpc_reqstate_free(rpc_state);
+	evhttp_send_error(req, HTTP_SERVUNAVAIL, "Service Error");
+	return;
+}
+
+/* Client implementation of RPC site */
+
+static int evrpc_schedule_request(struct evhttp_connection *connection,
+    struct evrpc_request_wrapper *ctx);
+
+struct evrpc_pool *
+evrpc_pool_new(struct event_base *base)
+{
+	struct evrpc_pool *pool = calloc(1, sizeof(struct evrpc_pool));
+	if (pool == NULL)
+		return (NULL);
+
+	TAILQ_INIT(&pool->connections);
+	TAILQ_INIT(&pool->requests);
+
+	TAILQ_INIT(&pool->input_hooks);
+	TAILQ_INIT(&pool->output_hooks);
+
+	pool->base = base;
+	pool->timeout = -1;
+
+	return (pool);
+}
+
+static void
+evrpc_request_wrapper_free(struct evrpc_request_wrapper *request)
+{
+	free(request->name);
+	free(request);
+}
+
+void
+evrpc_pool_free(struct evrpc_pool *pool)
+{
+	struct evhttp_connection *connection;
+	struct evrpc_request_wrapper *request;
+	struct evrpc_hook *hook;
+
+	while ((request = TAILQ_FIRST(&pool->requests)) != NULL) {
+		TAILQ_REMOVE(&pool->requests, request, next);
+		/* if this gets more complicated we need our own function */
+		evrpc_request_wrapper_free(request);
+	}
+
+	while ((connection = TAILQ_FIRST(&pool->connections)) != NULL) {
+		TAILQ_REMOVE(&pool->connections, connection, next);
+		evhttp_connection_free(connection);
+	}
+
+	while ((hook = TAILQ_FIRST(&pool->input_hooks)) != NULL) {
+		assert(evrpc_remove_hook(pool, INPUT, hook));
+	}
+
+	while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) {
+		assert(evrpc_remove_hook(pool, OUTPUT, hook));
+	}
+
+	free(pool);
+}
+
+/*
+ * Add a connection to the RPC pool.   A request scheduled on the pool
+ * may use any available connection.
+ */
+
+void
+evrpc_pool_add_connection(struct evrpc_pool *pool,
+    struct evhttp_connection *connection) {
+	assert(connection->http_server == NULL);
+	TAILQ_INSERT_TAIL(&pool->connections, connection, next);
+
+	/*
+	 * associate an event base with this connection
+	 */
+	if (pool->base != NULL)
+		evhttp_connection_set_base(connection, pool->base);
+
+	/* 
+	 * unless a timeout was specifically set for a connection,
+	 * the connection inherits the timeout from the pool.
+	 */
+	if (connection->timeout == -1)
+		connection->timeout = pool->timeout;
+
+	/* 
+	 * if we have any requests pending, schedule them with the new
+	 * connections.
+	 */
+
+	if (TAILQ_FIRST(&pool->requests) != NULL) {
+		struct evrpc_request_wrapper *request = 
+		    TAILQ_FIRST(&pool->requests);
+		TAILQ_REMOVE(&pool->requests, request, next);
+		evrpc_schedule_request(connection, request);
+	}
+}
+
+void
+evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs)
+{
+	struct evhttp_connection *evcon;
+	TAILQ_FOREACH(evcon, &pool->connections, next) {
+		evcon->timeout = timeout_in_secs;
+	}
+	pool->timeout = timeout_in_secs;
+}
+
+
+static void evrpc_reply_done(struct evhttp_request *, void *);
+static void evrpc_request_timeout(int, short, void *);
+
+/*
+ * Finds a connection object associated with the pool that is currently
+ * idle and can be used to make a request.
+ */
+static struct evhttp_connection *
+evrpc_pool_find_connection(struct evrpc_pool *pool)
+{
+	struct evhttp_connection *connection;
+	TAILQ_FOREACH(connection, &pool->connections, next) {
+		if (TAILQ_FIRST(&connection->requests) == NULL)
+			return (connection);
+	}
+
+	return (NULL);
+}
+
+/*
+ * We assume that the ctx is no longer queued on the pool.
+ */
+static int
+evrpc_schedule_request(struct evhttp_connection *connection,
+    struct evrpc_request_wrapper *ctx)
+{
+	struct evhttp_request *req = NULL;
+	struct evrpc_pool *pool = ctx->pool;
+	struct evrpc_status status;
+	char *uri = NULL;
+	int res = 0;
+
+	if ((req = evhttp_request_new(evrpc_reply_done, ctx)) == NULL)
+		goto error;
+
+	/* serialize the request data into the output buffer */
+	ctx->request_marshal(req->output_buffer, ctx->request);
+
+	uri = evrpc_construct_uri(ctx->name);
+	if (uri == NULL)
+		goto error;
+
+	/* we need to know the connection that we might have to abort */
+	ctx->evcon = connection;
+
+	/* apply hooks to the outgoing request */
+	if (evrpc_process_hooks(&pool->output_hooks,
+		req, req->output_buffer) == -1)
+		goto error;
+
+	if (pool->timeout > 0) {
+		/* 
+		 * a timeout after which the whole rpc is going to be aborted.
+		 */
+		struct timeval tv;
+		evutil_timerclear(&tv);
+		tv.tv_sec = pool->timeout;
+		evtimer_add(&ctx->ev_timeout, &tv);
+	}
+
+	/* start the request over the connection */
+	res = evhttp_make_request(connection, req, EVHTTP_REQ_POST, uri);
+	free(uri);
+
+	if (res == -1)
+		goto error;
+
+	return (0);
+
+error:
+	memset(&status, 0, sizeof(status));
+	status.error = EVRPC_STATUS_ERR_UNSTARTED;
+	(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
+	evrpc_request_wrapper_free(ctx);
+	return (-1);
+}
+
+int
+evrpc_make_request(struct evrpc_request_wrapper *ctx)
+{
+	struct evrpc_pool *pool = ctx->pool;
+
+	/* initialize the event structure for this rpc */
+	evtimer_set(&ctx->ev_timeout, evrpc_request_timeout, ctx);
+	if (pool->base != NULL)
+		event_base_set(pool->base, &ctx->ev_timeout);
+
+	/* we better have some available connections on the pool */
+	assert(TAILQ_FIRST(&pool->connections) != NULL);
+
+	/* 
+	 * if no connection is available, we queue the request on the pool,
+	 * the next time a connection is empty, the rpc will be send on that.
+	 */
+	TAILQ_INSERT_TAIL(&pool->requests, ctx, next);
+
+	evrpc_pool_schedule(pool);
+
+	return (0);
+}
+
+static void
+evrpc_reply_done(struct evhttp_request *req, void *arg)
+{
+	struct evrpc_request_wrapper *ctx = arg;
+	struct evrpc_pool *pool = ctx->pool;
+	struct evrpc_status status;
+	int res = -1;
+	
+	/* cancel any timeout we might have scheduled */
+	event_del(&ctx->ev_timeout);
+
+	memset(&status, 0, sizeof(status));
+	status.http_req = req;
+
+	/* we need to get the reply now */
+	if (req != NULL) {
+		/* apply hooks to the incoming request */
+		if (evrpc_process_hooks(&pool->input_hooks,
+			req, req->input_buffer) == -1) {
+			status.error = EVRPC_STATUS_ERR_HOOKABORTED;
+			res = -1;
+		} else {
+			res = ctx->reply_unmarshal(ctx->reply,
+			    req->input_buffer);
+			if (res == -1) {
+				status.error = EVRPC_STATUS_ERR_BADPAYLOAD;
+			}
+		}
+	} else {
+		status.error = EVRPC_STATUS_ERR_TIMEOUT;
+	}
+
+	if (res == -1) {
+		/* clear everything that we might have written previously */
+		ctx->reply_clear(ctx->reply);
+	}
+
+	(*ctx->cb)(&status, ctx->request, ctx->reply, ctx->cb_arg);
+	
+	evrpc_request_wrapper_free(ctx);
+
+	/* the http layer owns the request structure */
+
+	/* see if we can schedule another request */
+	evrpc_pool_schedule(pool);
+}
+
+static void
+evrpc_pool_schedule(struct evrpc_pool *pool)
+{
+	struct evrpc_request_wrapper *ctx = TAILQ_FIRST(&pool->requests);
+	struct evhttp_connection *evcon;
+
+	/* if no requests are pending, we have no work */
+	if (ctx == NULL)
+		return;
+
+	if ((evcon = evrpc_pool_find_connection(pool)) != NULL) {
+		TAILQ_REMOVE(&pool->requests, ctx, next);
+		evrpc_schedule_request(evcon, ctx);
+	}
+}
+
+static void
+evrpc_request_timeout(int fd, short what, void *arg)
+{
+	struct evrpc_request_wrapper *ctx = arg;
+	struct evhttp_connection *evcon = ctx->evcon;
+	assert(evcon != NULL);
+
+	evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
+}

=== added file 'extra/libevent/evrpc.h'
--- a/extra/libevent/evrpc.h	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/evrpc.h	2008-04-28 17:52:20 +0000
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2006 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _EVRPC_H_
+#define _EVRPC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @file evrpc.h
+ *
+ * This header files provides basic support for an RPC server and client.
+ *
+ * To support RPCs in a server, every supported RPC command needs to be
+ * defined and registered.
+ *
+ * EVRPC_HEADER(SendCommand, Request, Reply);
+ *
+ *  SendCommand is the name of the RPC command.
+ *  Request is the name of a structure generated by event_rpcgen.py.
+ *    It contains all parameters relating to the SendCommand RPC.  The
+ *    server needs to fill in the Reply structure.
+ *  Reply is the name of a structure generated by event_rpcgen.py.  It
+ *    contains the answer to the RPC.
+ *
+ * To register an RPC with an HTTP server, you need to first create an RPC
+ * base with:
+ *
+ *   struct evrpc_base *base = evrpc_init(http);
+ *
+ * A specific RPC can then be registered with
+ *
+ * EVRPC_REGISTER(base, SendCommand, Request, Reply,  FunctionCB, arg);
+ *
+ * when the server receives an appropriately formatted RPC, the user callback
+ * is invokved.   The callback needs to fill in the reply structure.
+ *
+ * void FunctionCB(EVRPC_STRUCT(SendCommand)* rpc, void *arg);
+ *
+ * To send the reply, call EVRPC_REQUEST_DONE(rpc);
+ *
+ * See the regression test for an example.
+ */
+
+struct evbuffer;
+struct event_base;
+struct evrpc_req_generic;
+
+/* Encapsulates a request */
+struct evrpc {
+	TAILQ_ENTRY(evrpc) next;
+
+	/* the URI at which the request handler lives */
+	const char* uri;
+
+	/* creates a new request structure */
+	void *(*request_new)(void);
+
+	/* frees the request structure */
+	void (*request_free)(void *);
+
+	/* unmarshals the buffer into the proper request structure */
+	int (*request_unmarshal)(void *, struct evbuffer *);
+
+	/* creates a new reply structure */
+	void *(*reply_new)(void);
+
+	/* creates a new reply structure */
+	void (*reply_free)(void *);
+
+	/* verifies that the reply is valid */
+	int (*reply_complete)(void *);
+	
+	/* marshals the reply into a buffer */
+	void (*reply_marshal)(struct evbuffer*, void *);
+
+	/* the callback invoked for each received rpc */
+	void (*cb)(struct evrpc_req_generic *, void *);
+	void *cb_arg;
+
+	/* reference for further configuration */
+	struct evrpc_base *base;
+};
+
+/** The type of a specific RPC Message
+ *
+ * @param rpcname the name of the RPC message
+ */
+#define EVRPC_STRUCT(rpcname) struct evrpc_req__##rpcname
+
+struct evhttp_request;
+struct evrpc_status;
+
+/* We alias the RPC specific structs to this voided one */
+struct evrpc_req_generic {
+	/* the unmarshaled request object */
+	void *request;
+
+	/* the empty reply object that needs to be filled in */
+	void *reply;
+
+	/* 
+	 * the static structure for this rpc; that can be used to
+	 * automatically unmarshal and marshal the http buffers.
+	 */
+	struct evrpc *rpc;
+
+	/*
+	 * the http request structure on which we need to answer.
+	 */
+	struct evhttp_request* http_req;
+
+	/*
+	 * callback to reply and finish answering this rpc
+	 */
+	void (*done)(struct evrpc_req_generic* rpc); 
+};
+
+/** Creates the definitions and prototypes for an RPC
+ *
+ * You need to use EVRPC_HEADER to create structures and function prototypes
+ * needed by the server and client implementation.  The structures have to be
+ * defined in an .rpc file and converted to source code via event_rpcgen.py
+ *
+ * @param rpcname the name of the RPC
+ * @param reqstruct the name of the RPC request structure
+ * @param replystruct the name of the RPC reply structure
+ * @see EVRPC_GENERATE()
+ */
+#define EVRPC_HEADER(rpcname, reqstruct, rplystruct) \
+EVRPC_STRUCT(rpcname) {	\
+	struct reqstruct* request; \
+	struct rplystruct* reply; \
+	struct evrpc* rpc; \
+	struct evhttp_request* http_req; \
+	void (*done)(struct evrpc_status *, \
+	    struct evrpc* rpc, void *request, void *reply);	     \
+};								     \
+int evrpc_send_request_##rpcname(struct evrpc_pool *, \
+    struct reqstruct *, struct rplystruct *, \
+    void (*)(struct evrpc_status *, \
+	struct reqstruct *, struct rplystruct *, void *cbarg),	\
+    void *);
+
+/** Generates the code for receiving and sending an RPC message
+ *
+ * EVRPC_GENERATE is used to create the code corresponding to sending
+ * and receiving a particular RPC message
+ *
+ * @param rpcname the name of the RPC
+ * @param reqstruct the name of the RPC request structure
+ * @param replystruct the name of the RPC reply structure
+ * @see EVRPC_HEADER()
+ */
+#define EVRPC_GENERATE(rpcname, reqstruct, rplystruct) \
+int evrpc_send_request_##rpcname(struct evrpc_pool *pool, \
+    struct reqstruct *request, struct rplystruct *reply, \
+    void (*cb)(struct evrpc_status *, \
+	struct reqstruct *, struct rplystruct *, void *cbarg),	\
+    void *cbarg) { \
+	struct evrpc_status status;				    \
+	struct evrpc_request_wrapper *ctx;			    \
+	ctx = (struct evrpc_request_wrapper *) \
+	    malloc(sizeof(struct evrpc_request_wrapper));	    \
+	if (ctx == NULL)					    \
+		goto error;					    \
+	ctx->pool = pool;					    \
+	ctx->evcon = NULL;					    \
+	ctx->name = strdup(#rpcname);				    \
+	if (ctx->name == NULL) {				    \
+		free(ctx);					    \
+		goto error;					    \
+	}							    \
+	ctx->cb = (void (*)(struct evrpc_status *, \
+		void *, void *, void *))cb;			    \
+	ctx->cb_arg = cbarg;					    \
+	ctx->request = (void *)request;				    \
+	ctx->reply = (void *)reply;				    \
+	ctx->request_marshal = (void (*)(struct evbuffer *, void *))reqstruct##_marshal; \
+	ctx->reply_clear = (void (*)(void *))rplystruct##_clear;    \
+	ctx->reply_unmarshal = (int (*)(void *, struct evbuffer *))rplystruct##_unmarshal; \
+	return (evrpc_make_request(ctx));			    \
+error:								    \
+	memset(&status, 0, sizeof(status));			    \
+	status.error = EVRPC_STATUS_ERR_UNSTARTED;		    \
+	(*(cb))(&status, request, reply, cbarg);		    \
+	return (-1);						    \
+}
+
+/** Provides access to the HTTP request object underlying an RPC
+ *
+ * Access to the underlying http object; can be used to look at headers or
+ * for getting the remote ip address
+ *
+ * @param rpc_req the rpc request structure provided to the server callback
+ * @return an struct evhttp_request object that can be inspected for
+ * HTTP headers or sender information.
+ */
+#define EVRPC_REQUEST_HTTP(rpc_req) (rpc_req)->http_req
+
+/** Creates the reply to an RPC request
+ * 
+ * EVRPC_REQUEST_DONE is used to answer a request; the reply is expected
+ * to have been filled in.  The request and reply pointers become invalid
+ * after this call has finished.
+ * 
+ * @param rpc_req the rpc request structure provided to the server callback
+ */
+#define EVRPC_REQUEST_DONE(rpc_req) do { \
+  struct evrpc_req_generic *_req = (struct evrpc_req_generic *)(rpc_req); \
+  _req->done(_req); \
+} while (0)
+  
+
+/* Takes a request object and fills it in with the right magic */
+#define EVRPC_REGISTER_OBJECT(rpc, name, request, reply) \
+  do { \
+    (rpc)->uri = strdup(#name); \
+    if ((rpc)->uri == NULL) {			 \
+      fprintf(stderr, "failed to register object\n");	\
+      exit(1);						\
+    } \
+    (rpc)->request_new = (void *(*)(void))request##_new; \
+    (rpc)->request_free = (void (*)(void *))request##_free; \
+    (rpc)->request_unmarshal = (int (*)(void *, struct evbuffer
*))request##_unmarshal; \
+    (rpc)->reply_new = (void *(*)(void))reply##_new; \
+    (rpc)->reply_free = (void (*)(void *))reply##_free; \
+    (rpc)->reply_complete = (int (*)(void *))reply##_complete; \
+    (rpc)->reply_marshal = (void (*)(struct evbuffer*, void *))reply##_marshal; \
+  } while (0)
+
+struct evrpc_base;
+struct evhttp;
+
+/* functions to start up the rpc system */
+
+/** Creates a new rpc base from which RPC requests can be received
+ *
+ * @param server a pointer to an existing HTTP server
+ * @return a newly allocated evrpc_base struct
+ * @see evrpc_free()
+ */
+struct evrpc_base *evrpc_init(struct evhttp *server);
+
+/** 
+ * Frees the evrpc base
+ *
+ * For now, you are responsible for making sure that no rpcs are ongoing.
+ *
+ * @param base the evrpc_base object to be freed
+ * @see evrpc_init
+ */
+void evrpc_free(struct evrpc_base *base);
+
+/** register RPCs with the HTTP Server
+ *
+ * registers a new RPC with the HTTP server, each RPC needs to have
+ * a unique name under which it can be identified.
+ *
+ * @param base the evrpc_base structure in which the RPC should be
+ *   registered.
+ * @param name the name of the RPC
+ * @param request the name of the RPC request structure
+ * @param reply the name of the RPC reply structure
+ * @param callback the callback that should be invoked when the RPC
+ * is received.  The callback has the following prototype
+ *   void (*callback)(EVRPC_STRUCT(Message)* rpc, void *arg)
+ * @param cbarg an additional parameter that can be passed to the callback.
+ *   The parameter can be used to carry around state.
+ */
+#define EVRPC_REGISTER(base, name, request, reply, callback, cbarg) \
+  do { \
+    struct evrpc* rpc = (struct evrpc *)calloc(1, sizeof(struct evrpc)); \
+    EVRPC_REGISTER_OBJECT(rpc, name, request, reply); \
+    evrpc_register_rpc(base, rpc, \
+	(void (*)(struct evrpc_req_generic*, void *))callback, cbarg);	\
+  } while (0)
+
+int evrpc_register_rpc(struct evrpc_base *, struct evrpc *,
+    void (*)(struct evrpc_req_generic*, void *), void *);
+
+/**
+ * Unregisters an already registered RPC
+ *
+ * @param base the evrpc_base object from which to unregister an RPC
+ * @param name the name of the rpc to unregister
+ * @return -1 on error or 0 when successful.
+ * @see EVRPC_REGISTER()
+ */
+#define EVRPC_UNREGISTER(base, name) evrpc_unregister_rpc(base, #name)
+
+int evrpc_unregister_rpc(struct evrpc_base *base, const char *name);
+
+/*
+ * Client-side RPC support
+ */
+
+struct evrpc_pool;
+struct evhttp_connection;
+
+/** 
+ * provides information about the completed RPC request.
+ */
+struct evrpc_status {
+#define EVRPC_STATUS_ERR_NONE		0
+#define EVRPC_STATUS_ERR_TIMEOUT	1
+#define EVRPC_STATUS_ERR_BADPAYLOAD	2
+#define EVRPC_STATUS_ERR_UNSTARTED	3
+#define EVRPC_STATUS_ERR_HOOKABORTED	4
+	int error;
+
+	/* for looking at headers or other information */
+	struct evhttp_request *http_req;
+};
+
+struct evrpc_request_wrapper {
+	TAILQ_ENTRY(evrpc_request_wrapper) next;
+
+        /* pool on which this rpc request is being made */
+        struct evrpc_pool *pool;
+
+        /* connection on which the request is being sent */
+	struct evhttp_connection *evcon;
+
+	/* event for implementing request timeouts */
+	struct event ev_timeout;
+
+	/* the name of the rpc */
+	char *name;
+
+	/* callback */
+	void (*cb)(struct evrpc_status*, void *request, void *reply, void *arg);
+	void *cb_arg;
+
+	void *request;
+	void *reply;
+
+	/* unmarshals the buffer into the proper request structure */
+	void (*request_marshal)(struct evbuffer *, void *);
+
+	/* removes all stored state in the reply */
+	void (*reply_clear)(void *);
+
+	/* marshals the reply into a buffer */
+	int (*reply_unmarshal)(void *, struct evbuffer*);
+};
+
+/** launches an RPC and sends it to the server
+ *
+ * EVRPC_MAKE_REQUEST() is used by the client to send an RPC to the server.
+ *
+ * @param name the name of the RPC
+ * @param pool the evrpc_pool that contains the connection objects over which
+ *   the request should be sent.
+ * @param request a pointer to the RPC request structure - it contains the
+ *   data to be sent to the server.
+ * @param reply a pointer to the RPC reply structure.  It is going to be filled
+ *   if the request was answered successfully
+ * @param cb the callback to invoke when the RPC request has been answered
+ * @param cbarg an additional argument to be passed to the client
+ * @return 0 on success, -1 on failure
+ */
+#define EVRPC_MAKE_REQUEST(name, pool, request, reply, cb, cbarg)	\
+	evrpc_send_request_##name(pool, request, reply, cb, cbarg)
+
+int evrpc_make_request(struct evrpc_request_wrapper *);
+
+/** creates an rpc connection pool
+ * 
+ * a pool has a number of connections associated with it.
+ * rpc requests are always made via a pool.
+ *
+ * @param base a pointer to an struct event_based object; can be left NULL
+ *   in singled-threaded applications
+ * @return a newly allocated struct evrpc_pool object
+ * @see evrpc_pool_free()
+ */
+struct evrpc_pool *evrpc_pool_new(struct event_base *base);
+/** frees an rpc connection pool
+ *
+ * @param pool a pointer to an evrpc_pool allocated via evrpc_pool_new()
+ * @see evrpc_pool_new()
+ */
+void evrpc_pool_free(struct evrpc_pool *pool);
+/*
+ * adds a connection over which rpc can be dispatched.  the connection
+ * object must have been newly created.
+ */
+void evrpc_pool_add_connection(struct evrpc_pool *, 
+    struct evhttp_connection *);
+
+/**
+ * Sets the timeout in secs after which a request has to complete.  The
+ * RPC is completely aborted if it does not complete by then.  Setting
+ * the timeout to 0 means that it never timeouts and can be used to
+ * implement callback type RPCs.
+ *
+ * Any connection already in the pool will be updated with the new
+ * timeout.  Connections added to the pool after set_timeout has be
+ * called receive the pool timeout only if no timeout has been set
+ * for the connection itself.
+ *
+ * @param pool a pointer to a struct evrpc_pool object
+ * @param timeout_in_secs the number of seconds after which a request should
+ *   timeout and a failure be returned to the callback.
+ */
+void evrpc_pool_set_timeout(struct evrpc_pool *pool, int timeout_in_secs);
+
+/**
+ * Hooks for changing the input and output of RPCs; this can be used to
+ * implement compression, authentication, encryption, ...
+ */
+
+enum EVRPC_HOOK_TYPE {
+	INPUT,		/**< apply the function to an input hook */
+	OUTPUT		/**< apply the function to an output hook */
+};
+
+/** adds a processing hook to either an rpc base or rpc pool
+ *
+ * If a hook returns -1, the processing is aborted.
+ *
+ * The add functions return handles that can be used for removing hooks.
+ *
+ * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
+ * @param hook_type either INPUT or OUTPUT
+ * @param cb the callback to call when the hook is activated
+ * @param cb_arg an additional argument for the callback
+ * @return a handle to the hook so it can be removed later
+ * @see evrpc_remove_hook()
+ */
+void *evrpc_add_hook(void *vbase,
+    enum EVRPC_HOOK_TYPE hook_type,
+    int (*cb)(struct evhttp_request *, struct evbuffer *, void *),
+    void *cb_arg);
+
+/** removes a previously added hook
+ *
+ * @param vbase a pointer to either struct evrpc_base or struct evrpc_pool
+ * @param hook_type either INPUT or OUTPUT
+ * @param handle a handle returned by evrpc_add_hook()
+ * @return 1 on success or 0 on failure
+ * @see evrpc_add_hook()
+ */
+int evrpc_remove_hook(void *vbase,
+    enum EVRPC_HOOK_TYPE hook_type,
+    void *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVRPC_H_ */

=== modified file 'extra/libevent/evsignal.h'
--- a/extra/libevent/evsignal.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/evsignal.h	2008-04-28 17:52:20 +0000
@@ -27,6 +27,8 @@
 #ifndef _EVSIGNAL_H_
 #define _EVSIGNAL_H_
 
+typedef void (*ev_sighandler_t)(int);
+
 struct evsignal_info {
 	struct event_list signalqueue;
 	struct event ev_signal;
@@ -34,6 +36,12 @@
 	int ev_signal_added;
 	volatile sig_atomic_t evsignal_caught;
 	sig_atomic_t evsigcaught[NSIG];
+#ifdef HAVE_SIGACTION
+	struct sigaction **sh_old;
+#else
+	ev_sighandler_t **sh_old;
+#endif
+	int sh_old_max;
 };
 void evsignal_init(struct event_base *);
 void evsignal_process(struct event_base *);

=== added file 'extra/libevent/evutil.c'
--- a/extra/libevent/evutil.c	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/evutil.c	2008-04-28 17:52:20 +0000
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2007 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include "misc.h"
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <errno.h>
+
+#include "evutil.h"
+#include "log.h"
+
+int
+evutil_socketpair(int family, int type, int protocol, int fd[2])
+{
+#ifndef WIN32
+	return socketpair(family, type, protocol, fd);
+#else
+	/* This code is originally from Tor.  Used with permission. */
+
+	/* This socketpair does not work when localhost is down. So
+	 * it's really not the same thing at all. But it's close enough
+	 * for now, and really, when localhost is down sometimes, we
+	 * have other problems too.
+	 */
+	int listener = -1;
+	int connector = -1;
+	int acceptor = -1;
+	struct sockaddr_in listen_addr;
+	struct sockaddr_in connect_addr;
+	int size;
+	int saved_errno = -1;
+
+	if (protocol
+#ifdef AF_UNIX
+		|| family != AF_UNIX
+#endif
+		) {
+		EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT);
+		return -1;
+	}
+	if (!fd) {
+		EVUTIL_SET_SOCKET_ERROR(WSAEINVAL);
+		return -1;
+	}
+
+	listener = socket(AF_INET, type, 0);
+	if (listener < 0)
+		return -1;
+	memset(&listen_addr, 0, sizeof(listen_addr));
+	listen_addr.sin_family = AF_INET;
+	listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	listen_addr.sin_port = 0;	/* kernel chooses port.	 */
+	if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
+		== -1)
+		goto tidy_up_and_fail;
+	if (listen(listener, 1) == -1)
+		goto tidy_up_and_fail;
+
+	connector = socket(AF_INET, type, 0);
+	if (connector < 0)
+		goto tidy_up_and_fail;
+	/* We want to find out the port number to connect to.  */
+	size = sizeof(connect_addr);
+	if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+		goto tidy_up_and_fail;
+	if (size != sizeof (connect_addr))
+		goto abort_tidy_up_and_fail;
+	if (connect(connector, (struct sockaddr *) &connect_addr,
+				sizeof(connect_addr)) == -1)
+		goto tidy_up_and_fail;
+
+	size = sizeof(listen_addr);
+	acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
+	if (acceptor < 0)
+		goto tidy_up_and_fail;
+	if (size != sizeof(listen_addr))
+		goto abort_tidy_up_and_fail;
+	EVUTIL_CLOSESOCKET(listener);
+	/* Now check we are talking to ourself by matching port and host on the
+	   two sockets.	 */
+	if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+		goto tidy_up_and_fail;
+	if (size != sizeof (connect_addr)
+		|| listen_addr.sin_family != connect_addr.sin_family
+		|| listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
+		|| listen_addr.sin_port != connect_addr.sin_port)
+		goto abort_tidy_up_and_fail;
+	fd[0] = connector;
+	fd[1] = acceptor;
+
+	return 0;
+
+ abort_tidy_up_and_fail:
+	saved_errno = WSAECONNABORTED;
+ tidy_up_and_fail:
+	if (saved_errno < 0)
+		saved_errno = WSAGetLastError();
+	if (listener != -1)
+		EVUTIL_CLOSESOCKET(listener);
+	if (connector != -1)
+		EVUTIL_CLOSESOCKET(connector);
+	if (acceptor != -1)
+		EVUTIL_CLOSESOCKET(acceptor);
+
+	EVUTIL_SET_SOCKET_ERROR(saved_errno);
+	return -1;
+#endif
+}
+
+int
+evutil_make_socket_nonblocking(int fd)
+{
+#ifdef WIN32
+	{
+		unsigned long nonblocking = 1;
+		ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking);
+	}
+#else
+	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+		event_warn("fcntl(O_NONBLOCK)");
+		return -1;
+}	
+#endif
+	return 0;
+}
+
+ev_int64_t
+evutil_strtoll(const char *s, char **endptr, int base)
+{
+#ifdef HAVE_STRTOLL
+	return (ev_int64_t)strtoll(s, endptr, base);
+#elif SIZEOF_LONG == 8
+	return (ev_int64_t)strtol(s, endptr, base);
+#elif defined(WIN32) && defined(_MSC_VER) && _MSC_VER < 1300
+	/* XXXX on old versions of MS APIs, we only support base
+	 * 10. */
+	ev_int64_t r;
+	if (base != 10)
+		return 0;
+	r = (ev_int64_t) _atoi64(s);
+	while (isspace(*s))
+		++s;
+	while (isdigit(*s))
+		++s;
+	if (endptr)
+		*endptr = (char*) s;
+	return r;
+#elif defined(WIN32)
+	return (ev_int64_t) _strtoi64(s, endptr, base);
+#else
+#error "I don't know how to parse 64-bit integers."
+#endif
+}

=== added file 'extra/libevent/evutil.h'
--- a/extra/libevent/evutil.h	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/evutil.h	2008-04-28 17:52:20 +0000
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2007 Niels Provos <provos@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _EVUTIL_H_
+#define _EVUTIL_H_
+
+/** @file evutil.h
+
+  Common convenience functions for cross-platform portability and
+  related socket manipulations.
+
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <event-config.h>
+#ifdef _EVENT_HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef _EVENT_HAVE_STDINT_H
+#include <stdint.h>
+#elif defined(_EVENT_HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+#ifdef _EVENT_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef _EVENT_HAVE_UINT64_T
+#define ev_uint64_t uint64_t
+#define ev_int64_t int64_t
+#elif defined(WIN32)
+#define ev_uint64_t __uint64_t
+#define ev_int64_t __int64_t
+#elif _EVENT_SIZEOF_LONG_LONG == 8
+#define ev_uint64_t unsigned long long
+#define ev_int64_t long long
+#elif _EVENT_SIZEOF_LONG == 8
+#define ev_uint64_t unsigned long
+#define ev_int64_t long
+#else
+#error "No way to define ev_uint64_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT32_T
+#define ev_uint32_t uint32_t
+#elif defined(WIN32)
+#define ev_uint32_t unsigned int
+#elif _EVENT_SIZEOF_LONG == 4
+#define ev_uint32_t unsigned long
+#elif _EVENT_SIZEOF_INT == 4
+#define ev_uint32_t unsigned int
+#else
+#error "No way to define ev_uint32_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT16_T
+#define ev_uint16_t uint16_t
+#elif defined(WIN32)
+#define ev_uint16_t unsigned short
+#elif _EVENT_SIZEOF_INT == 2
+#define ev_uint16_t unsigned int
+#elif _EVENT_SIZEOF_SHORT == 2
+#define ev_uint16_t unsigned short
+#else
+#error "No way to define ev_uint16_t"
+#endif
+
+#ifdef _EVENT_HAVE_UINT8_T
+#define ev_uint8_t uint8_t
+#else
+#define ev_uint8_t unsigned char
+#endif
+
+int evutil_socketpair(int d, int type, int protocol, int sv[2]);
+int evutil_make_socket_nonblocking(int sock);
+#ifdef WIN32
+#define EVUTIL_CLOSESOCKET(s) closesocket(s)
+#else
+#define EVUTIL_CLOSESOCKET(s) close(s)
+#endif
+
+#ifdef WIN32
+#define EVUTIL_SOCKET_ERROR() WSAGetLastError()
+#define EVUTIL_SET_SOCKET_ERROR(errcode)		\
+	do { WSASetLastError(errcode); } while (0)
+#else
+#define EVUTIL_SOCKET_ERROR() (errno)
+#define EVUTIL_SET_SOCKET_ERROR(errcode)		\
+		do { errno = (errcode); } while (0)
+#endif
+
+/*
+ * Manipulation functions for struct timeval
+ */
+#ifdef _EVENT_HAVE_TIMERADD
+#define evutil_timeradd(tvp, uvp, vvp) timeradd((tvp), (uvp), (vvp))
+#define evutil_timersub(tvp, uvp, vvp) timersub((tvp), (uvp), (vvp))
+#else
+#define evutil_timeradd(tvp, uvp, vvp)							\
+	do {														\
+		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;			\
+		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;       \
+		if ((vvp)->tv_usec >= 1000000) {						\
+			(vvp)->tv_sec++;									\
+			(vvp)->tv_usec -= 1000000;							\
+		}														\
+	} while (0)
+#define	evutil_timersub(tvp, uvp, vvp)						\
+	do {													\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec < 0) {							\
+			(vvp)->tv_sec--;								\
+			(vvp)->tv_usec += 1000000;						\
+		}													\
+	} while (0)
+#endif /* !_EVENT_HAVE_HAVE_TIMERADD */
+
+#ifdef _EVENT_HAVE_TIMERCLEAR
+#define evutil_timerclear(tvp) timerclear(tvp)
+#else
+#define	evutil_timerclear(tvp)	(tvp)->tv_sec = (tvp)->tv_usec = 0
+#endif
+
+#ifdef _EVENT_HAVE_TIMERCMP
+#define evutil_timercmp(tvp, uvp, cmp) timercmp((tvp), (uvp), cmp)
+#else
+#define	evutil_timercmp(tvp, uvp, cmp)							\
+	(((tvp)->tv_sec == (uvp)->tv_sec) ?							\
+	 ((tvp)->tv_usec cmp (uvp)->tv_usec) :						\
+	 ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#endif
+
+#ifdef _EVENT_HAVE_TIMERISSET
+#define evutil_timerisset(tvp) timerisset(tvp)
+#else
+#define	evutil_timerisset(tvp)	((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+
+/* big-int related functions */
+ev_int64_t evutil_strtoll(const char *s, char **endptr, int base);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EVUTIL_H_ */

=== modified file 'extra/libevent/http-internal.h'
--- a/extra/libevent/http-internal.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/http-internal.h	2008-04-28 17:52:20 +0000
@@ -35,9 +35,11 @@
 	EVCON_CONNECTED		/* connection is established */
 };
 
+struct event_base;
+
 struct evhttp_connection {
 	/* we use tailq only if they were created for an http server */
-	TAILQ_ENTRY(evhttp_connection) next;
+	TAILQ_ENTRY(evhttp_connection) (next);
 
 	int fd;
 	struct event ev;
@@ -45,7 +47,9 @@
 	struct evbuffer *input_buffer;
 	struct evbuffer *output_buffer;
 	
-	char *address;
+	char *bind_address;		/* address to use for binding the src */
+
+	char *address;			/* address to connect to */
 	u_short port;
 
 	int flags;
@@ -64,11 +68,13 @@
 
 	TAILQ_HEAD(evcon_requestq, evhttp_request) requests;
 	
-	void (*cb)(struct evhttp_connection *, void *);
+						   void (*cb)(struct evhttp_connection *, void *);
 	void *cb_arg;
 	
 	void (*closecb)(struct evhttp_connection *, void *);
 	void *closecb_arg;
+
+	struct event_base *base;
 };
 
 struct evhttp_cb {
@@ -80,16 +86,21 @@
 	void *cbarg;
 };
 
+/* both the http server as well as the rpc system need to queue connections */
+TAILQ_HEAD(evconq, evhttp_connection);
+
 struct evhttp {
 	struct event bind_ev;
 
 	TAILQ_HEAD(httpcbq, evhttp_cb) callbacks;
-        TAILQ_HEAD(evconq, evhttp_connection) connections;
+        struct evconq connections;
 
         int timeout;
 
 	void (*gencb)(struct evhttp_request *req, void *);
 	void *gencbarg;
+
+	struct event_base *base;
 };
 
 /* resets the connection; can be reused for more requests */

=== modified file 'extra/libevent/http.c'
--- a/extra/libevent/http.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/http.c	2008-04-28 17:52:20 +0000
@@ -25,11 +25,15 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
 #endif
 
 #ifdef HAVE_SYS_TIME_H
@@ -46,7 +50,6 @@
 #include <sys/wait.h>
 #endif
 
-#include <sys/tree.h>
 #include <sys/queue.h>
 
 #ifndef WIN32
@@ -54,6 +57,10 @@
 #include <netdb.h>
 #endif
 
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
@@ -65,8 +72,12 @@
 #endif
 #include <signal.h>
 #include <time.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
+#endif
 
 #undef timeout_pending
 #undef timeout_initialized
@@ -74,9 +85,17 @@
 #include "strlcpy-internal.h"
 #include "event.h"
 #include "evhttp.h"
+#include "evutil.h"
 #include "log.h"
 #include "http-internal.h"
 
+#ifdef WIN32
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define strdup _strdup
+#endif
+
 #ifndef HAVE_GETADDRINFO
 struct addrinfo {
 	int ai_family;
@@ -89,17 +108,28 @@
 static int
 fake_getaddrinfo(const char *hostname, struct addrinfo *ai)
 {
-	struct hostent *he;
-	he = gethostbyname(hostname);
-	if (!he)
-		return (-1);
-	ai->ai_family = he->h_addrtype;
+	struct hostent *he = NULL;
+	struct sockaddr_in *sa;
+	if (hostname) {
+		he = gethostbyname(hostname);
+		if (!he)
+			return (-1);
+	}
+	ai->ai_family = he ? he->h_addrtype : AF_INET;
 	ai->ai_socktype = SOCK_STREAM;
 	ai->ai_protocol = 0;
-	ai->ai_addrlen = he->h_length;
+	ai->ai_addrlen = sizeof(struct sockaddr_in);
 	if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen)))
 		return (-1);
-	memcpy(ai->ai_addr, &he->h_addr_list[0], ai->ai_addrlen);
+	sa = (struct sockaddr_in*)ai->ai_addr;
+	memset(sa, 0, ai->ai_addrlen);
+	if (he) {
+		sa->sin_family = he->h_addrtype;
+		memcpy(&sa->sin_addr, he->h_addr_list[0], he->h_length);
+	} else {
+		sa->sin_family = AF_INET;
+		sa->sin_addr.s_addr = INADDR_ANY;
+	}
 	ai->ai_next = NULL;
 	return (0);
 }
@@ -114,28 +144,16 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #endif
 
-static int
-event_make_socket_nonblocking(int fd)
-{
-
-#ifdef WIN32
-	{
-		unsigned long nonblocking = 1;
-		ioctlsocket(fd, FIONBIO, (unsigned long*) &nonblocking);
-	}
-#else
-	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
-		event_warn("fcntl(O_NONBLOCK)");
-		return -1;
-	}
-#endif
-	return 0;
-}
+/* wrapper for setting the base from the http server */
+#define EVHTTP_BASE_SET(x, y) do { \
+	if ((x)->base != NULL) event_base_set((x)->base, y);	\
+} while (0) 
 
 extern int debug;
 
-static int make_socket_ai(int should_bind, struct addrinfo *);
-static int make_socket(int should_bind, const char *, u_short);
+static int socket_connect(int fd, const char *address, unsigned short port);
+static int bind_socket_ai(struct addrinfo *);
+static int bind_socket(const char *, u_short);
 static void name_from_addr(struct sockaddr *, socklen_t, char **, char **);
 static int evhttp_associate_new_request_with_connection(
 	struct evhttp_connection *evcon);
@@ -149,27 +167,29 @@
 void evhttp_write(int, short, void *);
 
 #ifndef HAVE_STRSEP
+/* strsep replacement for platforms that lack it.  Only works if
+ * del is one character long. */
 static char *
 strsep(char **s, const char *del)
 {
 	char *d, *tok;
+	assert(strlen(del) == 1);
 	if (!s || !*s)
 		return NULL;
 	tok = *s;
 	d = strstr(tok, del);
-	if (d)
-		*s = d + strlen(del);
-	else
+	if (d) {
+		*d = '\0';
+		*s = d + 1;
+	} else
 		*s = NULL;
 	return tok;
 }
 #endif
 
 static const char *
-html_replace(char ch)
+html_replace(char ch, char *buf)
 {
-	static char buf[2];
-	
 	switch (ch) {
 	case '<':
 		return "&lt;";
@@ -204,15 +224,16 @@
 {
 	int i, new_size = 0, old_size = strlen(html);
 	char *escaped_html, *p;
+	char scratch_space[2];
 	
 	for (i = 0; i < old_size; ++i)
-		new_size += strlen(html_replace(html[i]));
+          new_size += strlen(html_replace(html[i], scratch_space));
 
 	p = escaped_html = malloc(new_size + 1);
 	if (escaped_html == NULL)
 		event_err(1, "%s: malloc(%d)", __func__, new_size + 1);
 	for (i = 0; i < old_size; ++i) {
-		const char *replaced = html_replace(html[i]);
+		const char *replaced = html_replace(html[i], scratch_space);
 		/* this is length checked */
 		strcpy(p, replaced);
 		p += strlen(replaced);
@@ -223,7 +244,7 @@
 	return (escaped_html);
 }
 
-const char *
+static const char *
 evhttp_method(enum evhttp_cmd_type type)
 {
 	const char *method;
@@ -252,7 +273,7 @@
 	if (timeout != 0) {
 		struct timeval tv;
 		
-		timerclear(&tv);
+		evutil_timerclear(&tv);
 		tv.tv_sec = timeout != -1 ? timeout : default_timeout;
 		event_add(ev, &tv);
 	} else {
@@ -275,17 +296,18 @@
 		event_del(&evcon->ev);
 
 	event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon);
+	EVHTTP_BASE_SET(evcon, &evcon->ev);
 	evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT);
 }
 
 /*
- * Create the headers need for an HTTP reply
+ * Create the headers need for an HTTP request
  */
 static void
 evhttp_make_header_request(struct evhttp_connection *evcon,
     struct evhttp_request *req)
 {
-	static char line[1024];
+	char line[1024];
 	const char *method;
 	
 	evhttp_remove_header(req->output_headers, "Accept-Encoding");
@@ -328,59 +350,77 @@
 	    && strncasecmp(connection, "keep-alive", 10) == 0);
 }
 
+static void
+evhttp_maybe_add_date_header(struct evkeyvalq *headers)
+{
+	if (evhttp_find_header(headers, "Date") == NULL) {
+		char date[50];
+#ifndef WIN32
+		struct tm cur;
+#endif
+		struct tm *cur_p;
+		time_t t = time(NULL);
+#ifdef WIN32
+		cur_p = gmtime(&t);
+#else
+		gmtime_r(&t, &cur);
+		cur_p = &cur;
+#endif
+		if (strftime(date, sizeof(date),
+			"%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
+			evhttp_add_header(headers, "Date", date);
+		}
+	}
+}
+
+static void
+evhttp_maybe_add_content_length_header(struct evkeyvalq *headers,
+    long content_length)
+{
+	if (evhttp_find_header(headers, "Transfer-Encoding") == NULL &&
+	    evhttp_find_header(headers,	"Content-Length") == NULL) {
+		char len[12];
+		snprintf(len, sizeof(len), "%ld", content_length);
+		evhttp_add_header(headers, "Content-Length", len);
+	}
+}
+
 /*
  * Create the headers needed for an HTTP reply
  */
+
 static void
 evhttp_make_header_response(struct evhttp_connection *evcon,
     struct evhttp_request *req)
 {
-	static char line[1024];
+	char line[1024];
 	snprintf(line, sizeof(line), "HTTP/%d.%d %d %s\r\n",
 	    req->major, req->minor, req->response_code,
 	    req->response_code_line);
 	evbuffer_add(evcon->output_buffer, line, strlen(line));
 
+	if (req->major == 1 && req->minor == 1)
+		evhttp_maybe_add_date_header(req->output_headers);
+
+	if (req->major == 1 && 
+	    (req->minor == 1 || 
+		evhttp_is_connection_keepalive(req->input_headers))) {
+		/* 
+		 * we need to add the content length if the user did
+		 * not give it, this is required for persistent
+		 * connections to work.
+		 */
+		evhttp_maybe_add_content_length_header(req->output_headers,
+		    (long)EVBUFFER_LENGTH(req->output_buffer));
+	}
+
 	/* Potentially add headers for unidentified content. */
 	if (EVBUFFER_LENGTH(req->output_buffer)) {
 		if (evhttp_find_header(req->output_headers,
-							   "Date") == NULL) {
-			char date[50];
-			struct tm cur, *cur_p;
-			time_t t = time(NULL);
-#ifdef WIN32
-			cur_p = gmtime(&t);
-#else
-			gmtime_r(&t, &cur);
-			cur_p = &cur;
-#endif
-			if (strftime(date, sizeof(date),
-						 "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
-				evhttp_add_header(req->output_headers, "Date", date);
-			}
-		}
-
-		if (evhttp_find_header(req->output_headers,
 			"Content-Type") == NULL) {
 			evhttp_add_header(req->output_headers,
 			    "Content-Type", "text/html; charset=ISO-8859-1");
 		}
-
-		/* 
-		 * we need to add the content length if the user did
-		 * not give it, this is required for persistent
-		 * connections to work.
-		 */
-		if (evhttp_find_header(req->output_headers,
-			"Transfer-Encoding") == NULL &&
-		    evhttp_find_header(req->output_headers,
-			"Content-Length") == NULL) {
-			static char len[12];
-			snprintf(len, sizeof(len), "%ld",
-			    (long)EVBUFFER_LENGTH(req->output_buffer));
-			evhttp_add_header(req->output_headers,
-			    "Content-Length", len);
-		}
 	}
 
 	/* if the request asked for a close, we send a close, too */
@@ -395,7 +435,7 @@
 void
 evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
 {
-	static char line[1024];
+	char line[1024];
 	struct evkeyval *header;
 
 	/*
@@ -415,7 +455,7 @@
 	}
 	evbuffer_add(evcon->output_buffer, "\r\n", 2);
 
-	if (EVBUFFER_LENGTH(req->output_buffer) >= 0) {
+	if (EVBUFFER_LENGTH(req->output_buffer) > 0) {
 		/*
 		 * For a request, we add the POST data, for a reply, this
 		 * is the regular data.
@@ -429,9 +469,11 @@
 int
 evhttp_hostportfile(char *url, char **phost, u_short *pport, char **pfile)
 {
+	/* XXX not threadsafe. */
 	static char host[1024];
 	static char file[1024];
-	char *p, *p2;
+	char *p;
+	const char *p2;
 	int len;
 	u_short port;
 
@@ -590,12 +632,12 @@
 		(*evcon->cb)(evcon, evcon->cb_arg);
 }
 
-void
+static void
 evhttp_connection_done(struct evhttp_connection *evcon)
 {
 	struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
 	int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
-    
+
 	/*
 	 * if this is an incoming connection, we need to leave the request
 	 * on the connection, so that we can reply to it.
@@ -666,7 +708,7 @@
 				free(p);
 				continue;
 			}
-			req->ntoread = strtol(p, &endp, 16);
+			req->ntoread = evutil_strtoll(p, &endp, 16);
 			error = *p == '\0' || (*endp != '\0' && *endp != ' ');
 			free(p);
 			if (error) {
@@ -699,7 +741,7 @@
 	return (0);
 }
 
-void
+static void
 evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
 {
 	struct evbuffer *buf = evcon->input_buffer;
@@ -730,6 +772,7 @@
 	}
 	/* Read more! */
 	event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon);
+	EVHTTP_BASE_SET(evcon, &evcon->ev);
 	evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
 }
 
@@ -768,7 +811,7 @@
 	evhttp_read_body(evcon, req);
 }
 
-void
+static void
 evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
 {
 	/* This is after writing the request to the server */
@@ -814,7 +857,10 @@
 		event_del(&evcon->ev);
 	
 	if (evcon->fd != -1)
-		close(evcon->fd);
+		EVUTIL_CLOSESOCKET(evcon->fd);
+
+	if (evcon->bind_address != NULL)
+		free(evcon->bind_address);
 
 	if (evcon->address != NULL)
 		free(evcon->address);
@@ -828,6 +874,18 @@
 	free(evcon);
 }
 
+void
+evhttp_connection_set_local_address(struct evhttp_connection *evcon,
+    const char *address)
+{
+	assert(evcon->state == EVCON_DISCONNECTED);
+	if (evcon->bind_address)
+		free(evcon->bind_address);
+	if ((evcon->bind_address = strdup(address)) == NULL)
+		event_err(1, "%s: strdup", __func__);
+}
+
+
 static void
 evhttp_request_dispatch(struct evhttp_connection* evcon)
 {
@@ -861,7 +919,7 @@
 		if (evcon->state == EVCON_CONNECTED && evcon->closecb != NULL)
 			(*evcon->closecb)(evcon, evcon->closecb_arg);
 
-		close(evcon->fd);
+		EVUTIL_CLOSESOCKET(evcon->fd);
 		evcon->fd = -1;
 	}
 	evcon->state = EVCON_DISCONNECTED;
@@ -886,6 +944,7 @@
 		event_del(&evcon->close_ev);
 	event_set(&evcon->close_ev, evcon->fd, EV_READ,
 	    evhttp_detect_close_cb, evcon);
+	EVHTTP_BASE_SET(evcon, &evcon->close_ev);
 	event_add(&evcon->close_ev, NULL);
 }
 
@@ -952,6 +1011,7 @@
  cleanup:
 	if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
 		evtimer_set(&evcon->ev, evhttp_connection_retry, evcon);
+		EVHTTP_BASE_SET(evcon, &evcon->ev);
 		evhttp_add_event(&evcon->ev, MIN(3600, 2 << evcon->retry_cnt),
 		    HTTP_CONNECT_TIMEOUT);
 		evcon->retry_cnt++;
@@ -975,7 +1035,7 @@
  * Check if we got a valid response code.
  */
 
-int
+static int
 evhttp_valid_response_code(int code)
 {
 	if (code == 0)
@@ -986,7 +1046,7 @@
 
 /* Parses the status line of a web server */
 
-int
+static int
 evhttp_parse_response_line(struct evhttp_request *req, char *line)
 {
 	char *protocol;
@@ -1028,7 +1088,7 @@
 
 /* Parse the first line of a HTTP request */
 
-int
+static int
 evhttp_parse_request_line(struct evhttp_request *req, char *line)
 {
 	char *method;
@@ -1144,10 +1204,12 @@
 {
 	struct evkeyval *header = NULL;
 
+	event_debug(("%s: key: %s val: %s\n", __func__, key, value));
+
 	if (strchr(value, '\r') != NULL || strchr(value, '\n') != NULL ||
 	    strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
 		/* drop illegal headers */
-		event_debug(("%s: dropping illegal header\n"));
+		event_debug(("%s: dropping illegal header\n", __func__));
 		return (-1);
 	}
 
@@ -1186,58 +1248,55 @@
 int
 evhttp_parse_lines(struct evhttp_request *req, struct evbuffer* buffer)
 {
-	u_char *endp;
+	char *line;
 	int done = 0;
 
 	struct evkeyvalq* headers = req->input_headers;
-	while ((endp = evbuffer_find(buffer, (u_char *)"\r\n", 2)) != NULL) {
+	while ((line = evbuffer_readline(buffer)) != NULL) {
 		char *skey, *svalue;
 
-		if (strncmp((char *)EVBUFFER_DATA(buffer), "\r\n", 2) == 0) {
-			evbuffer_drain(buffer, 2);
-			/* Last header - Done */
+		if (*line == '\0') { /* Last header - Done */
 			done = 1;
+			free (line);
 			break;
 		}
 
-		*endp = '\0';
-		endp += 2;
-
 		/* Processing of header lines */
 		if (req->got_firstline == 0) {
 			switch (req->kind) {
 			case EVHTTP_REQUEST:
-				if (evhttp_parse_request_line(req,
-					(char *)EVBUFFER_DATA(buffer)) == -1)
-					return (-1);
+				if (evhttp_parse_request_line(req, line) == -1)
+					goto error;
 				break;
 			case EVHTTP_RESPONSE:
-				if (evhttp_parse_response_line(req,
-					(char *)EVBUFFER_DATA(buffer)) == -1)
-					return (-1);
+				if (evhttp_parse_response_line(req, line) == -1)
+					goto error;
 				break;
 			default:
-				return (-1);
+				goto error;
 			}
 			req->got_firstline = 1;
 		} else {
 			/* Regular header */
-			svalue = (char *)EVBUFFER_DATA(buffer);
+			svalue = line;
 			skey = strsep(&svalue, ":");
 			if (svalue == NULL)
-				return (-1);
+				goto error;
 
 			svalue += strspn(svalue, " ");
 
 			if (evhttp_add_header(headers, skey, svalue) == -1)
-				return (-1);
+				goto error;
 		}
 
-		/* Move the uncompleted headers forward */
-		evbuffer_drain(buffer, endp - EVBUFFER_DATA(buffer));
+		free (line);
 	}
 
 	return (done);
+
+ error:
+	free (line);
+	return (-1);
 }
 
 static int
@@ -1263,7 +1322,7 @@
 		req->ntoread = -1;
 	} else {
 		char *endp;
-		req->ntoread = strtol(content_length, &endp, 10);
+		req->ntoread = evutil_strtoll(content_length, &endp, 10);
 		if (*content_length == '\0' || *endp != '\0') {
 			event_warnx("%s: illegal content length: %s",
 			    __func__, content_length);
@@ -1423,6 +1482,14 @@
 	return (NULL);
 }
 
+void evhttp_connection_set_base(struct evhttp_connection *evcon,
+    struct event_base *base)
+{
+	assert(evcon->base == NULL);
+	assert(evcon->state == EVCON_DISCONNECTED);
+	evcon->base = base;
+}
+
 void
 evhttp_connection_set_timeout(struct evhttp_connection *evcon,
     int timeout_in_secs)
@@ -1464,16 +1531,21 @@
 	assert(!(evcon->flags & EVHTTP_CON_INCOMING));
 	evcon->flags |= EVHTTP_CON_OUTGOING;
 	
-	/* Do async connection to HTTP server */
-	if ((evcon->fd = make_socket(
-		     0, evcon->address, evcon->port)) == -1) {
-		event_debug(("%s: failed to connect to \"%s:%d\"",
-			__func__, evcon->address, evcon->port));
+	evcon->fd = bind_socket(evcon->bind_address, 0);
+	if (evcon->fd == -1) {
+		event_debug(("%s: failed to bind to \"%s\"",
+			__func__, evcon->bind_address));
+		return (-1);
+	}
+
+	if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) {
+		EVUTIL_CLOSESOCKET(evcon->fd); evcon->fd = -1;
 		return (-1);
 	}
 
 	/* Set up a callback for successful connection setup */
 	event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_connectioncb, evcon);
+	EVHTTP_BASE_SET(evcon, &evcon->ev);
 	evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_CONNECT_TIMEOUT);
 
 	evcon->state = EVCON_CONNECTING;
@@ -1539,11 +1611,12 @@
 	if (event_initialized(&evcon->ev))
 		event_del(&evcon->ev);
 	event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read_header, evcon);
+	EVHTTP_BASE_SET(evcon, &evcon->ev);
 	
 	evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT);
 }
 
-void
+static void
 evhttp_send_done(struct evhttp_connection *evcon, void *arg)
 {
 	int need_close;
@@ -1579,12 +1652,12 @@
 void
 evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
 {
-	char *fmt = "<HTML><HEAD>\n"
-	    "<TITLE>%d %s</TITLE>\n"
-	    "</HEAD><BODY>\n"
-	    "<H1>Method Not Implemented</H1>\n"
-	    "Invalid method in request<P>\n"
-	    "</BODY></HTML>\n";
+#define ERR_FORMAT "<HTML><HEAD>\n" \
+	    "<TITLE>%d %s</TITLE>\n" \
+	    "</HEAD><BODY>\n" \
+	    "<H1>Method Not Implemented</H1>\n" \
+	    "Invalid method in request<P>\n" \
+	    "</BODY></HTML>\n"
 
 	struct evbuffer *buf = evbuffer_new();
 
@@ -1593,11 +1666,12 @@
 
 	evhttp_response_code(req, error, reason);
 
-	evbuffer_add_printf(buf, fmt, error, reason);
+	evbuffer_add_printf(buf, ERR_FORMAT, error, reason);
 
 	evhttp_send_page(req, buf);
 
 	evbuffer_free(buf);
+#undef ERR_FORMAT
 }
 
 /* Requires that headers and response code are already set up */
@@ -1609,7 +1683,7 @@
 
 	assert(TAILQ_FIRST(&evcon->requests) == req);
 
-	/* xxx: not sure if we really should expost the data buffer this way */
+	/* xxx: not sure if we really should expose the data buffer this way */
 	if (databuf != NULL)
 		evbuffer_add_buffer(req->output_buffer, databuf);
 	
@@ -1652,9 +1726,12 @@
 {
 	if (req->chunked) {
 		evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n",
-		    EVBUFFER_LENGTH(databuf));
+				    (unsigned)EVBUFFER_LENGTH(databuf));
 	}
 	evbuffer_add_buffer(req->evcon->output_buffer, databuf);
+	if (req->chunked) {
+		evbuffer_add(req->evcon->output_buffer, "\r\n", 2);
+	}
 	evhttp_write_buffer(req->evcon, NULL, NULL);
 }
 
@@ -1759,8 +1836,9 @@
 	
 	ret = malloc(strlen(uri) + 1);
 	if (ret == NULL)
-		event_err(1, "%s: malloc(%d)", __func__, strlen(uri) + 1);
-
+		event_err(1, "%s: malloc(%lu)", __func__,
+			  (unsigned long)(strlen(uri) + 1));
+	
 	for (i = j = 0; uri[i] != '\0'; i++) {
 		c = uri[i];
 		if (c == '?') {
@@ -1855,7 +1933,7 @@
 	return (NULL);
 }
 
-void
+static void
 evhttp_handle_request(struct evhttp_request *req, void *arg)
 {
 	struct evhttp *http = arg;
@@ -1877,25 +1955,26 @@
 		return;
 	} else {
 		/* We need to send a 404 here */
-		char *fmt = "<html><head>"
-		    "<title>404 Not Found</title>"
-		    "</head><body>"
-		    "<h1>Not Found</h1>"
-		    "<p>The requested URL %s was not found on this server.</p>"
-		    "</body></html>\n";
+#define ERR_FORMAT "<html><head>" \
+		    "<title>404 Not Found</title>" \
+		    "</head><body>" \
+		    "<h1>Not Found</h1>" \
+		    "<p>The requested URL %s was not found on this server.</p>"\
+		    "</body></html>\n"
 
 		char *escaped_html = evhttp_htmlescape(req->uri);
 		struct evbuffer *buf = evbuffer_new();
 
 		evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
 
-		evbuffer_add_printf(buf, fmt, escaped_html);
+		evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
 
 		free(escaped_html);
-		
+
 		evhttp_send_page(req, buf);
 
 		evbuffer_free(buf);
+#undef ERR_FORMAT
 	}
 }
 
@@ -1911,28 +1990,30 @@
 		event_warn("%s: bad accept", __func__);
 		return;
 	}
-        if (event_make_socket_nonblocking(nfd) < 0)
-                return;
+	if (evutil_make_socket_nonblocking(nfd) < 0)
+		return;
 
 	evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen);
 }
 
-static int
-bind_socket(struct evhttp *http, const char *address, u_short port)
+int
+evhttp_bind_socket(struct evhttp *http, const char *address, u_short port)
 {
 	struct event *ev = &http->bind_ev;
 	int fd;
 
-	if ((fd = make_socket(1, address, port)) == -1)
+	if ((fd = bind_socket(address, port)) == -1)
 		return (-1);
 
 	if (listen(fd, 10) == -1) {
 		event_warn("%s: listen", __func__);
+		EVUTIL_CLOSESOCKET(fd);
 		return (-1);
 	}
 
 	/* Schedule the socket for accepting */
 	event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http);
+	EVHTTP_BASE_SET(http, ev);
 	event_add(ev, NULL);
 
 	event_debug(("Bound to port %d - Awaiting connections ... ", port));
@@ -1940,14 +2021,10 @@
 	return (0);
 }
 
-/*
- * Start a web server on the specified address and port.
- */
-
-struct evhttp *
-evhttp_start(const char *address, u_short port)
+static struct evhttp*
+evhttp_new_object(void)
 {
-	struct evhttp *http;
+	struct evhttp *http = NULL;
 
 	if ((http = calloc(1, sizeof(struct evhttp))) == NULL) {
 		event_warn("%s: calloc", __func__);
@@ -1959,7 +2036,29 @@
 	TAILQ_INIT(&http->callbacks);
 	TAILQ_INIT(&http->connections);
 
-	if (bind_socket(http, address, port) == -1) {
+	return (http);
+}
+
+struct evhttp *
+evhttp_new(struct event_base *base)
+{
+	struct evhttp *http = evhttp_new_object();
+
+	http->base = base;
+
+	return (http);
+}
+
+/*
+ * Start a web server on the specified address and port.
+ */
+
+struct evhttp *
+evhttp_start(const char *address, u_short port)
+{
+	struct evhttp *http = evhttp_new_object();
+
+	if (evhttp_bind_socket(http, address, port) == -1) {
 		free(http);
 		return (NULL);
 	}
@@ -1976,7 +2075,7 @@
 
 	/* Remove the accepting part */
 	event_del(&http->bind_ev);
-	close(fd);
+	EVUTIL_CLOSESOCKET(fd);
 
 	while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
 		/* evhttp_connection_free removes the connection */
@@ -2142,6 +2241,7 @@
 
 static struct evhttp_connection*
 evhttp_get_request_connection(
+	struct evhttp* http,
 	int fd, struct sockaddr *sa, socklen_t salen)
 {
 	struct evhttp_connection *evcon;
@@ -2154,6 +2254,10 @@
 	/* we need a connection object to put the http request on */
 	if ((evcon = evhttp_connection_new(hostname, atoi(portname))) == NULL)
 		return (NULL);
+
+	/* associate the base if we have one*/
+	evhttp_connection_set_base(evcon, http->base);
+
 	evcon->flags |= EVHTTP_CON_INCOMING;
 	evcon->state = EVCON_CONNECTED;
 	
@@ -2192,7 +2296,7 @@
 {
 	struct evhttp_connection *evcon;
 
-	evcon = evhttp_get_request_connection(fd, sa, salen);
+	evcon = evhttp_get_request_connection(http, fd, sa, salen);
 	if (evcon == NULL)
 		return;
 
@@ -2224,7 +2328,7 @@
         struct addrinfo ai, *aitop;
         int ai_result;
 
-        memset(&ai, 0, sizeof (ai));
+        memset(&ai, 0, sizeof(ai));
         ai.ai_family = AF_INET;
         ai.ai_socktype = SOCK_RAW;
         ai.ai_flags = 0;
@@ -2238,7 +2342,7 @@
 	return (aitop);
 #else
 	assert(0);
-	return NULL; // XXXXX Use gethostbyname, if this function is ever used.
+	return NULL; /* XXXXX Use gethostbyname, if this function is ever used. */
 #endif
 }
 #endif
@@ -2248,6 +2352,7 @@
     char **phost, char **pport)
 {
 #ifdef HAVE_GETNAMEINFO
+	/* XXXX not threadsafe. */
 	static char ntop[NI_MAXHOST];
 	static char strport[NI_MAXSERV];
 	int ni_result;
@@ -2264,16 +2369,15 @@
 	*phost = ntop;
 	*pport = strport;
 #else
-	// XXXX
+	/* XXXX */
 #endif
 }
 
 /* Either connect or bind */
 
 static int
-make_socket_ai(int should_bind, struct addrinfo *ai)
+bind_socket_ai(struct addrinfo *ai)
 {
-        struct linger linger;
         int fd, on = 1, r;
 	int serrno;
 
@@ -2284,7 +2388,7 @@
                 return (-1);
         }
 
-        if (event_make_socket_nonblocking(fd) < 0)
+        if (evutil_make_socket_nonblocking(fd) < 0)
                 goto out;
 
 #ifndef WIN32
@@ -2296,67 +2400,68 @@
 
         setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
-        linger.l_onoff = 1;
-        linger.l_linger = 5;
-        setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger));
 
-	if (should_bind)
-		r = bind(fd, ai->ai_addr, ai->ai_addrlen);
-	else
-		r = connect(fd, ai->ai_addr, ai->ai_addrlen);
-	if (r == -1) {
-#ifdef WIN32
-		int tmp_error = WSAGetLastError();
-		if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL &&
-		    tmp_error != WSAEINPROGRESS) {
-			goto out;
-		}
-#else
-		if (errno != EINPROGRESS) {
-			goto out;
-		}
-#endif
-	}
+	r = bind(fd, ai->ai_addr, ai->ai_addrlen);
+	if (r == -1)
+		goto out;
 
 	return (fd);
 
  out:
-	serrno = errno;
-	close(fd);
-	errno = serrno;
+	serrno = EVUTIL_SOCKET_ERROR();
+	EVUTIL_CLOSESOCKET(fd);
+	EVUTIL_SET_SOCKET_ERROR(serrno);
 	return (-1);
 }
 
-static int
-make_socket(int should_bind, const char *address, u_short port)
+static struct addrinfo *
+make_addrinfo(const char *address, u_short port)
 {
-	int fd;
-        struct addrinfo ai, *aitop = NULL;
+        struct addrinfo *aitop = NULL;
+
 #ifdef HAVE_GETADDRINFO
+        struct addrinfo ai;
         char strport[NI_MAXSERV];
         int ai_result;
 
-        memset(&ai, 0, sizeof (ai));
+        memset(&ai, 0, sizeof(ai));
         ai.ai_family = AF_INET;
         ai.ai_socktype = SOCK_STREAM;
-        ai.ai_flags = should_bind ? AI_PASSIVE : 0;
-        snprintf(strport, sizeof (strport), "%d", port);
+        ai.ai_flags = AI_PASSIVE;  /* turn NULL host name into INADDR_ANY */
+        snprintf(strport, sizeof(strport), "%d", port);
         if ((ai_result = getaddrinfo(address, strport, &ai, &aitop)) != 0) {
                 if ( ai_result == EAI_SYSTEM )
                         event_warn("getaddrinfo");
                 else
                         event_warnx("getaddrinfo: %s", gai_strerror(ai_result));
-		return (-1);
+		return (NULL);
         }
 #else
-	if (fake_getaddrinfo(address, &ai) < 0) {
+	static int cur;
+	static struct addrinfo ai[2]; /* We will be returning the address of some of this memory
so it has to last even after this call. */
+	if (++cur == 2) cur = 0;   /* allow calling this function twice */
+
+	if (fake_getaddrinfo(address, &ai[cur]) < 0) {
 		event_warn("fake_getaddrinfo");
-		return (-1);
+		return (NULL);
 	}
-	aitop = &ai;
+	aitop = &ai[cur];
+	((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port);
 #endif
 
-	fd = make_socket_ai(should_bind, aitop);
+	return (aitop);
+}
+
+static int
+bind_socket(const char *address, u_short port)
+{
+	int fd;
+	struct addrinfo *aitop = make_addrinfo(address, port);
+
+	if (aitop == NULL)
+		return (-1);
+
+	fd = bind_socket_ai(aitop);
 
 #ifdef HAVE_GETADDRINFO
 	freeaddrinfo(aitop);
@@ -2366,3 +2471,42 @@
 
 	return (fd);
 }
+
+static int
+socket_connect(int fd, const char *address, unsigned short port)
+{
+	struct addrinfo *ai = make_addrinfo(address, port);
+	int res = -1;
+
+	if (ai == NULL) {
+		event_debug(("%s: make_addrinfo: \"%s:%d\"",
+			__func__, address, port));
+		return (-1);
+	}
+
+	if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+#ifdef WIN32
+		int tmp_error = WSAGetLastError();
+		if (tmp_error != WSAEWOULDBLOCK && tmp_error != WSAEINVAL &&
+		    tmp_error != WSAEINPROGRESS) {
+			goto out;
+		}
+#else
+		if (errno != EINPROGRESS) {
+			goto out;
+		}
+#endif
+	}
+
+	/* everything is fine */
+	res = 0;
+
+out:
+#ifdef HAVE_GETADDRINFO
+	freeaddrinfo(ai);
+#else
+	fake_freeaddrinfo(ai);
+#endif
+
+	return (res);
+}

=== modified file 'extra/libevent/kqueue.c'
--- a/extra/libevent/kqueue.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/kqueue.c	2008-04-28 17:52:20 +0000
@@ -51,16 +51,18 @@
 #endif
 
 /* Some platforms apparently define the udata field of struct kevent as
- * ntptr_t, whereas others define it as void*.  There doesn't seem to be an
+ * intptr_t, whereas others define it as void*.  There doesn't seem to be an
  * easy way to tell them apart via autoconf, so we need to use OS macros. */
 #if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) &&
!defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__)
-#define PTR_TO_UDATA(x) ((intptr_t)(x))
+#define PTR_TO_UDATA(x)	((intptr_t)(x))
 #else
-#define PTR_TO_UDATA(x) (x)
+#define PTR_TO_UDATA(x)	(x)
 #endif
 
 #include "event.h"
+#include "event-internal.h"
 #include "log.h"
+#include "event-internal.h"
 
 #define EVLIST_X_KQINKERNEL	0x1000
 
@@ -72,27 +74,27 @@
 	struct kevent *events;
 	int nevents;
 	int kq;
+	pid_t pid;
 };
 
-void *kq_init	(struct event_base *);
-int kq_add	(void *, struct event *);
-int kq_del	(void *, struct event *);
-int kq_recalc	(struct event_base *, void *, int);
-int kq_dispatch	(struct event_base *, void *, struct timeval *);
-int kq_insert	(struct kqop *, struct kevent *);
-void kq_dealloc (struct event_base *, void *);
+static void *kq_init	(struct event_base *);
+static int kq_add	(void *, struct event *);
+static int kq_del	(void *, struct event *);
+static int kq_dispatch	(struct event_base *, void *, struct timeval *);
+static int kq_insert	(struct kqop *, struct kevent *);
+static void kq_dealloc (struct event_base *, void *);
 
 const struct eventop kqops = {
 	"kqueue",
 	kq_init,
 	kq_add,
 	kq_del,
-	kq_recalc,
 	kq_dispatch,
-	kq_dealloc
+	kq_dealloc,
+	1 /* need reinit */
 };
 
-void *
+static void *
 kq_init(struct event_base *base)
 {
 	int kq;
@@ -115,6 +117,8 @@
 
 	kqueueop->kq = kq;
 
+	kqueueop->pid = getpid();
+
 	/* Initalize fields */
 	kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
 	if (kqueueop->changes == NULL) {
@@ -153,13 +157,7 @@
 	return (kqueueop);
 }
 
-int
-kq_recalc(struct event_base *base, void *arg, int max)
-{
-	return (0);
-}
-
-int
+static int
 kq_insert(struct kqop *kqop, struct kevent *kev)
 {
 	int nevents = kqop->nevents;
@@ -210,7 +208,7 @@
 	/* Do nothing here */
 }
 
-int
+static int
 kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 {
 	struct kqop *kqop = arg;
@@ -277,7 +275,7 @@
 			continue;
 
 		if (!(ev->ev_events & EV_PERSIST))
-			event_del(ev);
+			ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
 
 		event_active(ev, which,
 		    ev->ev_events & EV_SIGNAL ? events[i].data : 1);
@@ -287,7 +285,7 @@
 }
 
 
-int
+static int
 kq_add(void *arg, struct event *ev)
 {
 	struct kqop *kqop = arg;
@@ -295,6 +293,7 @@
 
 	if (ev->ev_events & EV_SIGNAL) {
 		int nsignal = EVENT_SIGNAL(ev);
+                struct timespec timeout = { 0, 0 };
 
  		memset(&kev, 0, sizeof(kev));
 		kev.ident = nsignal;
@@ -303,11 +302,14 @@
 		if (!(ev->ev_events & EV_PERSIST))
 			kev.flags |= EV_ONESHOT;
 		kev.udata = PTR_TO_UDATA(ev);
-		
-		if (kq_insert(kqop, &kev) == -1)
-			return (-1);
-
-		if (signal(nsignal, kq_sighandler) == SIG_ERR)
+
+		/* Be ready for the signal if it is sent any time between
+		 * now and the next call to kq_dispatch. */
+                if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
+                	return (-1);
+
+		if (_evsignal_set_handler(ev->ev_base, nsignal,
+					  kq_sighandler) == -1)
 			return (-1);
 
 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
@@ -351,7 +353,7 @@
 	return (0);
 }
 
-int
+static int
 kq_del(void *arg, struct event *ev)
 {
 	struct kqop *kqop = arg;
@@ -371,7 +373,7 @@
 		if (kq_insert(kqop, &kev) == -1)
 			return (-1);
 
-		if (signal(nsignal, SIG_DFL) == SIG_ERR)
+		if (_evsignal_restore_handler(ev->ev_base, nsignal) == -1)
 			return (-1);
 
 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
@@ -405,7 +407,7 @@
 	return (0);
 }
 
-void
+static void
 kq_dealloc(struct event_base *base, void *arg)
 {
 	struct kqop *kqop = arg;
@@ -414,7 +416,7 @@
 		free(kqop->changes);
 	if (kqop->events)
 		free(kqop->events);
-	if (kqop->kq)
+	if (kqop->kq >= 0 && kqop->pid == getpid())
 		close(kqop->kq);
 	memset(kqop, 0, sizeof(struct kqop));
 	free(kqop);

=== modified file 'extra/libevent/log.c'
--- a/extra/libevent/log.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/log.c	2008-04-28 17:52:20 +0000
@@ -48,7 +48,6 @@
 #include "misc.h"
 #endif
 #include <sys/types.h>
-#include <sys/tree.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #else

=== modified file 'extra/libevent/log.h'
--- a/extra/libevent/log.h	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/log.h	2008-04-28 17:52:20 +0000
@@ -27,12 +27,18 @@
 #ifndef _LOG_H_
 #define _LOG_H_
 
-void event_err(int eval, const char *fmt, ...);
-void event_warn(const char *fmt, ...);
-void event_errx(int eval, const char *fmt, ...);
-void event_warnx(const char *fmt, ...);
-void event_msgx(const char *fmt, ...);
-void _event_debugx(const char *fmt, ...);
+#ifdef __GNUC__
+#define EV_CHECK_FMT(a,b) __attribute__((format(printf, a, b)))
+#else
+#define EV_CHECK_FMT(a,b)
+#endif
+
+void event_err(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
+void event_warn(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void event_errx(int eval, const char *fmt, ...) EV_CHECK_FMT(2,3);
+void event_warnx(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void event_msgx(const char *fmt, ...) EV_CHECK_FMT(1,2);
+void _event_debugx(const char *fmt, ...) EV_CHECK_FMT(1,2);
 
 #ifdef USE_DEBUG
 #define event_debug(x) _event_debugx x
@@ -40,4 +46,6 @@
 #define event_debug(x) do {;} while (0)
 #endif
 
+#undef EV_CHECK_FMT
+
 #endif

=== added file 'extra/libevent/min_heap.h'
--- a/extra/libevent/min_heap.h	1970-01-01 00:00:00 +0000
+++ b/extra/libevent/min_heap.h	2008-04-28 17:52:20 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@stripped>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _MIN_HEAP_H_
+#define _MIN_HEAP_H_
+
+#include "event.h"
+
+typedef struct min_heap
+{
+    struct event** p;
+    unsigned n, a;
+} min_heap_t;
+
+static inline void           min_heap_ctor(min_heap_t* s);
+static inline void           min_heap_dtor(min_heap_t* s);
+static inline void           min_heap_elem_init(struct event* e);
+static inline int            min_heap_elem_greater(struct event *a, struct event *b);
+static inline int            min_heap_empty(min_heap_t* s);
+static inline unsigned       min_heap_size(min_heap_t* s);
+static inline struct event*  min_heap_top(min_heap_t* s);
+static inline int            min_heap_reserve(min_heap_t* s, unsigned n);
+static inline int            min_heap_push(min_heap_t* s, struct event* e);
+static inline struct event*  min_heap_pop(min_heap_t* s);
+static inline int            min_heap_erase(min_heap_t* s, struct event* e);
+static inline void           min_heap_shift_up_(min_heap_t* s, unsigned hole_index,
struct event* e);
+static inline void           min_heap_shift_down_(min_heap_t* s, unsigned hole_index,
struct event* e);
+
+int min_heap_elem_greater(struct event *a, struct event *b)
+{
+    return timercmp(&a->ev_timeout, &b->ev_timeout, >);
+}
+
+void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; }
+void min_heap_dtor(min_heap_t* s) { free(s->p); }
+void min_heap_elem_init(struct event* e) { e->min_heap_idx = -1; }
+int min_heap_empty(min_heap_t* s) { return 0u == s->n; }
+unsigned min_heap_size(min_heap_t* s) { return s->n; }
+struct event* min_heap_top(min_heap_t* s) { return s->n ? *s->p : 0; }
+
+int min_heap_push(min_heap_t* s, struct event* e)
+{
+    if(min_heap_reserve(s, s->n + 1))
+        return -1;
+    min_heap_shift_up_(s, s->n++, e);
+    return 0;
+}
+
+struct event* min_heap_pop(min_heap_t* s)
+{
+    if(s->n)
+    {
+        struct event* e = *s->p;
+        e->min_heap_idx = -1;
+        min_heap_shift_down_(s, 0u, s->p[--s->n]);
+        return e;
+    }
+    return 0;
+}
+
+int min_heap_erase(min_heap_t* s, struct event* e)
+{
+    if(((unsigned int)-1) != e->min_heap_idx)
+    {
+        min_heap_shift_down_(s, e->min_heap_idx, s->p[--s->n]);
+        e->min_heap_idx = -1;
+        return 0;
+    }
+    return -1;
+}
+
+int min_heap_reserve(min_heap_t* s, unsigned n)
+{
+    if(s->a < n)
+    {
+        struct event** p;
+        unsigned a = s->a ? s->a * 2 : 8;
+        if(a < n)
+            a = n;
+        if(!(p = (struct event**)realloc(s->p, a * sizeof *p)))
+            return -1;
+        s->p = p;
+        s->a = a;
+    }
+    return 0;
+}
+
+void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct event* e)
+{
+    unsigned parent = (hole_index - 1) / 2;
+    while(hole_index && min_heap_elem_greater(s->p[parent], e))
+    {
+        (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index;
+        hole_index = parent;
+        parent = (hole_index - 1) / 2;
+    }
+    (s->p[hole_index] = e)->min_heap_idx = hole_index;
+}
+
+void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct event* e)
+{
+    unsigned min_child = 2 * (hole_index + 1);
+    while(min_child <= s->n)
+	{
+        min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child],
s->p[min_child - 1]);
+        if(!(min_heap_elem_greater(e, s->p[min_child])))
+            break;
+        (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index;
+        hole_index = min_child;
+        min_child = 2 * (hole_index + 1);
+	}
+    min_heap_shift_up_(s, hole_index,  e);
+}
+
+#endif /* _MIN_HEAP_H_ */

=== modified file 'extra/libevent/poll.c'
--- a/extra/libevent/poll.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/poll.c	2008-04-28 17:52:20 +0000
@@ -39,7 +39,6 @@
 #include <sys/_time.h>
 #endif
 #include <sys/queue.h>
-#include <sys/tree.h>
 #include <poll.h>
 #include <signal.h>
 #include <stdio.h>
@@ -68,24 +67,23 @@
 			      * "no entry." */
 };
 
-void *poll_init	(struct event_base *);
-int poll_add		(void *, struct event *);
-int poll_del		(void *, struct event *);
-int poll_recalc		(struct event_base *, void *, int);
-int poll_dispatch	(struct event_base *, void *, struct timeval *);
-void poll_dealloc	(struct event_base *, void *);
+static void *poll_init	(struct event_base *);
+static int poll_add		(void *, struct event *);
+static int poll_del		(void *, struct event *);
+static int poll_dispatch	(struct event_base *, void *, struct timeval *);
+static void poll_dealloc	(struct event_base *, void *);
 
 const struct eventop pollops = {
 	"poll",
 	poll_init,
 	poll_add,
 	poll_del,
-	poll_recalc,
 	poll_dispatch,
-	poll_dealloc
+	poll_dealloc,
+    0
 };
 
-void *
+static void *
 poll_init(struct event_base *base)
 {
 	struct pollop *pollop;
@@ -102,17 +100,6 @@
 	return (pollop);
 }
 
-/*
- * Called with the highest fd that we know about.  If it is 0, completely
- * recalculate everything.
- */
-
-int
-poll_recalc(struct event_base *base, void *arg, int max)
-{
-	return (0);
-}
-
 #ifdef CHECK_INVARIANTS
 static void
 poll_check_ok(struct pollop *pop)
@@ -147,7 +134,7 @@
 #define poll_check_ok(pop)
 #endif
 
-int
+static int
 poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 {
 	int res, i, msec = -1, nfds;
@@ -201,13 +188,9 @@
 			continue;
 
 		if (r_ev && (res & r_ev->ev_events)) {
-			if (!(r_ev->ev_events & EV_PERSIST))
-				event_del(r_ev);
 			event_active(r_ev, res & r_ev->ev_events, 1);
 		}
 		if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
-			if (!(w_ev->ev_events & EV_PERSIST))
-				event_del(w_ev);
 			event_active(w_ev, res & w_ev->ev_events, 1);
 		}
 	}
@@ -215,7 +198,7 @@
 	return (0);
 }
 
-int
+static int
 poll_add(void *arg, struct event *ev)
 {
 	struct pollop *pop = arg;
@@ -320,7 +303,7 @@
  * Nothing to be done here.
  */
 
-int
+static int
 poll_del(void *arg, struct event *ev)
 {
 	struct pollop *pop = arg;
@@ -373,7 +356,7 @@
 	return (0);
 }
 
-void
+static void
 poll_dealloc(struct event_base *base, void *arg)
 {
 	struct pollop *pop = arg;

=== modified file 'extra/libevent/select.c'
--- a/extra/libevent/select.c	2007-11-15 21:06:20 +0000
+++ b/extra/libevent/select.c	2008-04-28 17:52:20 +0000
@@ -42,7 +42,6 @@
 #include <sys/select.h>
 #endif
 #include <sys/queue.h>
-#include <sys/tree.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -73,26 +72,25 @@
 	struct event **event_w_by_fd;
 };
 
-void *select_init	(struct event_base *);
-int select_add		(void *, struct event *);
-int select_del		(void *, struct event *);
-int select_recalc	(struct event_base *, void *, int);
-int select_dispatch	(struct event_base *, void *, struct timeval *);
-void select_dealloc     (struct event_base *, void *);
+static void *select_init	(struct event_base *);
+static int select_add		(void *, struct event *);
+static int select_del		(void *, struct event *);
+static int select_dispatch	(struct event_base *, void *, struct timeval *);
+static void select_dealloc     (struct event_base *, void *);
 
 const struct eventop selectops = {
 	"select",
 	select_init,
 	select_add,
 	select_del,
-	select_recalc,
 	select_dispatch,
-	select_dealloc
+	select_dealloc,
+	0
 };
 
 static int select_resize(struct selectop *sop, int fdsz);
 
-void *
+static void *
 select_init(struct event_base *base)
 {
 	struct selectop *sop;
@@ -138,22 +136,7 @@
 #define check_selectop(sop) do { (void) sop; } while (0)
 #endif
 
-/*
- * Called with the highest fd that we know about.  If it is 0, completely
- * recalculate everything.
- */
-
-int
-select_recalc(struct event_base *base, void *arg, int max)
-{
-	struct selectop *sop = arg;
-
-	check_selectop(sop);
-
-	return (0);
-}
-
-int
+static int
 select_dispatch(struct event_base *base, void *arg, struct timeval *tv)
 {
 	int res, i;
@@ -198,13 +181,9 @@
 			res |= EV_WRITE;
 		}
 		if (r_ev && (res & r_ev->ev_events)) {
-			if (!(r_ev->ev_events & EV_PERSIST))
-				event_del(r_ev);
 			event_active(r_ev, res & r_ev->ev_events, 1);
 		}
 		if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
-			if (!(w_ev->ev_events & EV_PERSIST))
-				event_del(w_ev);
 			event_active(w_ev, res & w_ev->ev_events, 1);
 		}
 	}
@@ -273,7 +252,7 @@
 }
 
 
-int
+static int
 select_add(void *arg, struct event *ev)
 {
 	struct selectop *sop = arg;
@@ -323,7 +302,7 @@
  * Nothing to be done here.
  */
 
-int
+static int
 select_del(void *arg, struct event *ev)
 {
 	struct selectop *sop = arg;
@@ -351,7 +330,7 @@
 	return (0);
 }
 
-void
+static void
 select_dealloc(struct event_base *base, void *arg)
 {
 	struct selectop *sop = arg;

=== modified file 'extra/libevent/signal.c'
--- a/extra/libevent/signal.c	2007-11-01 18:37:56 +0000
+++ b/extra/libevent/signal.c	2008-04-28 17:52:20 +0000
@@ -30,20 +30,27 @@
 #include "config.h"
 #endif
 
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
 #include <sys/types.h>
-#include <sys/tree.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
-#else
-#include <sys/_time.h>
 #endif
 #include <sys/queue.h>
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
+#endif
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <errno.h>
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
@@ -53,6 +60,7 @@
 #include "event.h"
 #include "event-internal.h"
 #include "evsignal.h"
+#include "evutil.h"
 #include "log.h"
 
 struct event_base *evsignal_base = NULL;
@@ -64,13 +72,15 @@
 evsignal_cb(int fd, short what, void *arg)
 {
 	static char signals[100];
-	struct event *ev = arg;
+#ifdef WIN32
+	SSIZE_T n;
+#else
 	ssize_t n;
+#endif
 
-	n = read(fd, signals, sizeof(signals));
+	n = recv(fd, signals, sizeof(signals), 0);
 	if (n == -1)
 		event_err(1, "%s: read", __func__);
-	event_add(ev, NULL);
 }
 
 #ifdef HAVE_SETFD
@@ -90,55 +100,146 @@
 	 * pair to wake up our event loop.  The event loop then scans for
 	 * signals that got delivered.
 	 */
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
+	if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1)
 		event_err(1, "%s: socketpair", __func__);
 
 	FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
 	FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
+	base->sig.sh_old = NULL;
+	base->sig.sh_old_max = 0;
 	base->sig.evsignal_caught = 0;
 	memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
 
-	fcntl(base->sig.ev_signal_pair[0], F_SETFL, O_NONBLOCK);
+        evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
 
-	event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], EV_READ,
-	    evsignal_cb, &base->sig.ev_signal);
+	event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
+		EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
 	base->sig.ev_signal.ev_base = base;
 	base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
 }
 
+/* Helper: set the signal handler for evsignal to handler in base, so that
+ * we can restore the original handler when we clear the current one. */
+int
+_evsignal_set_handler(struct event_base *base,
+		      int evsignal, void (*handler)(int))
+{
+#ifdef HAVE_SIGACTION
+	struct sigaction sa;
+#else
+	ev_sighandler_t sh;
+#endif
+	struct evsignal_info *sig = &base->sig;
+	void *p;
+
+	/*
+	 * resize saved signal handler array up to the highest signal number.
+	 * a dynamic array is used to keep footprint on the low side.
+	 */
+	if (evsignal >= sig->sh_old_max) {
+		event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
+			    __func__, evsignal, sig->sh_old_max));
+		sig->sh_old_max = evsignal + 1;
+		p = realloc(sig->sh_old, sig->sh_old_max * sizeof *sig->sh_old);
+		if (p == NULL) {
+			event_warn("realloc");
+			return (-1);
+		}
+		sig->sh_old = p;
+	}
+
+	/* allocate space for previous handler out of dynamic array */
+	sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
+	if (sig->sh_old[evsignal] == NULL) {
+		event_warn("malloc");
+		return (-1);
+	}
+
+	/* save previous handler and setup new handler */
+#ifdef HAVE_SIGACTION
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = handler;
+	sa.sa_flags |= SA_RESTART;
+	sigfillset(&sa.sa_mask);
+
+	if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
+		event_warn("sigaction");
+		free(sig->sh_old[evsignal]);
+		return (-1);
+	}
+#else
+	if ((sh = signal(evsignal, handler)) == SIG_ERR) {
+		event_warn("signal");
+		free(sig->sh_old[evsignal]);
+		return (-1);
+	}
+	*sig->sh_old[evsignal] = sh;
+#endif
+
+	return (0);
+}
+
 int
 evsignal_add(struct event *ev)
 {
 	int evsignal;
-	struct sigaction sa;
 	struct event_base *base = ev->ev_base;
+	struct evsignal_info *sig = &ev->ev_base->sig;
 
 	if (ev->ev_events & (EV_READ|EV_WRITE))
 		event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
 	evsignal = EVENT_SIGNAL(ev);
 
-	memset(&sa, 0, sizeof(sa));
-	sa.sa_handler = evsignal_handler;
-	sigfillset(&sa.sa_mask);
-	sa.sa_flags |= SA_RESTART;
+	event_debug(("%s: %p: changing signal handler", __func__, ev));
+	if (_evsignal_set_handler(base, evsignal, evsignal_handler) == -1)
+		return (-1);
+
 	/* catch signals if they happen quickly */
 	evsignal_base = base;
 
-	if (sigaction(evsignal, &sa, NULL) == -1)
-		return (-1);
-
-	if (!base->sig.ev_signal_added) {
-		base->sig.ev_signal_added = 1;
-		event_add(&base->sig.ev_signal, NULL);
+	if (!sig->ev_signal_added) {
+		sig->ev_signal_added = 1;
+		event_add(&sig->ev_signal, NULL);
 	}
 
 	return (0);
 }
 
 int
+_evsignal_restore_handler(struct event_base *base, int evsignal)
+{
+	int ret = 0;
+	struct evsignal_info *sig = &base->sig;
+#ifdef HAVE_SIGACTION
+	struct sigaction *sh;
+#else
+	ev_sighandler_t *sh;
+#endif
+
+	/* restore previous handler */
+	sh = sig->sh_old[evsignal];
+	sig->sh_old[evsignal] = NULL;
+#ifdef HAVE_SIGACTION
+	if (sigaction(evsignal, sh, NULL) == -1) {
+		event_warn("sigaction");
+		ret = -1;
+	}
+#else
+	if (signal(evsignal, *sh) == SIG_ERR) {
+		event_warn("signal");
+		ret = -1;
+	}
+#endif
+	free(sh);
+
+	return ret;
+}
+
+int
 evsignal_del(struct event *ev)
 {
-	return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
+	event_debug(("%s: %p: restoring signal handler", __func__, ev));
+	return _evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev));
 }
 
 static void
@@ -148,7 +249,7 @@
 
 	if(evsignal_base == NULL) {
 		event_warn(
-			"%s: received signal %s, but have no base configured",
+			"%s: received signal %d, but have no base configured",
 			__func__, sig);
 		return;
 	}
@@ -156,8 +257,12 @@
 	evsignal_base->sig.evsigcaught[sig]++;
 	evsignal_base->sig.evsignal_caught = 1;
 
+#ifndef HAVE_SIGACTION
+	signal(sig, evsignal_handler);
+#endif
+
 	/* Wake up our notification mechanism */
-	write(evsignal_base->sig.ev_signal_pair[0], "a", 1);
+	send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
 	errno = save_errno;
 }
 
@@ -188,8 +293,12 @@
 	}
 	assert(TAILQ_EMPTY(&base->sig.signalqueue));
 
-	close(base->sig.ev_signal_pair[0]);
+	EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]);
 	base->sig.ev_signal_pair[0] = -1;
-	close(base->sig.ev_signal_pair[1]);
+	EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[1]);
 	base->sig.ev_signal_pair[1] = -1;
+	base->sig.sh_old_max = 0;
+
+	/* per index frees are handled in evsignal_del() */
+	free(base->sig.sh_old);
 }

=== modified file 'include/errmsg.h'
--- a/include/errmsg.h	2007-04-12 22:56:22 +0000
+++ b/include/errmsg.h	2008-05-20 16:36:26 +0000
@@ -96,6 +96,7 @@
 #define CR_NOT_IMPLEMENTED                      2054
 #define CR_SERVER_LOST_EXTENDED			2055
 #define CR_STMT_CLOSED				2056
-#define CR_ERROR_LAST  /*Copy last error nr:*/  2056
+#define CR_NEW_STMT_METADATA                    2057
+#define CR_ERROR_LAST  /*Copy last error nr:*/  2057
 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */
 

=== modified file 'include/my_global.h'
--- a/include/my_global.h	2008-05-08 20:43:28 +0000
+++ b/include/my_global.h	2008-05-21 10:17:29 +0000
@@ -559,7 +559,7 @@
 
 #define CMP_NUM(a,b)    (((a) < (b)) ? -1 : ((a) == (b)) ? 0 : 1)
 #define sgn(a)		(((a) < 0) ? -1 : ((a) > 0) ? 1 : 0)
-#define swap_variables(t, a, b) { register t dummy; dummy= a; a= b; b= dummy; }
+#define swap_variables(t, a, b) { t dummy; dummy= a; a= b; b= dummy; }
 #define test(a)		((a) ? 1 : 0)
 #define set_if_bigger(a,b)  do { if ((a) < (b)) (a)=(b); } while(0)
 #define set_if_smaller(a,b) do { if ((a) > (b)) (a)=(b); } while(0)

=== modified file 'include/my_sys.h'
--- a/include/my_sys.h	2008-05-08 16:01:15 +0000
+++ b/include/my_sys.h	2008-05-21 10:17:29 +0000
@@ -90,6 +90,8 @@
 #define ME_COLOUR2	((2 << ME_HIGHBYTE))
 #define ME_COLOUR3	((3 << ME_HIGHBYTE))
 #define ME_FATALERROR   1024    /* Fatal statement error */
+#define ME_NO_WARNING_FOR_ERROR 2048 /* Don't push a warning for error */
+#define ME_NO_SP_HANDLER 4096 /* Don't call stored routine error handlers */
 
 	/* Bits in last argument to fn_format */
 #define MY_REPLACE_DIR		1	/* replace dir in name with 'dir' */

=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h	2008-04-01 15:13:57 +0000
+++ b/include/mysql_com.h	2008-05-21 10:17:29 +0000
@@ -194,25 +194,44 @@
 #define SERVER_MORE_RESULTS_EXISTS 8    /* Multi query - next query exists */
 #define SERVER_QUERY_NO_GOOD_INDEX_USED 16
 #define SERVER_QUERY_NO_INDEX_USED      32
-/*
+/**
   The server was able to fulfill the clients request and opened a
   read-only non-scrollable cursor for a query. This flag comes
   in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
 */
 #define SERVER_STATUS_CURSOR_EXISTS 64
-/*
+/**
   This flag is sent when a read-only cursor is exhausted, in reply to
   COM_STMT_FETCH command.
 */
 #define SERVER_STATUS_LAST_ROW_SENT 128
 #define SERVER_STATUS_DB_DROPPED        256 /* A database was dropped */
 #define SERVER_STATUS_NO_BACKSLASH_ESCAPES 512
+/**
+  Sent to the client if after a prepared statement reprepare
+  we discovered that the new statement returns a different 
+  number of result set columns.
+*/
+#define SERVER_STATUS_METADATA_CHANGED 1024
 /*
   Tell clients that this query was logged to the slow query log.
   Not yet set in the server, but interface is defined for applications
   to use.  See WorkLog 4098.
 */
-#define SERVER_QUERY_WAS_SLOW           1024
+#define SERVER_QUERY_WAS_SLOW           2048
+
+/**
+  Server status flags that must be cleared when starting
+  execution of a new SQL statement.
+  Flags from this set are only added to the
+  current server status by the execution engine, but 
+  never removed -- the execution engine expects them 
+  to disappear automagically by the next command.
+*/
+#define SERVER_STATUS_CLEAR_SET (SERVER_QUERY_NO_GOOD_INDEX_USED| \
+                                 SERVER_QUERY_NO_INDEX_USED|\
+                                 SERVER_MORE_RESULTS_EXISTS|\
+                                 SERVER_STATUS_METADATA_CHANGED)
 
 #define MYSQL_ERRMSG_SIZE	512
 #define NET_READ_TIMEOUT	30		/* Timeout on read */
@@ -221,6 +240,7 @@
 
 #define ONLY_KILL_QUERY         1
 
+
 struct st_vio;					/* Only C */
 typedef struct st_vio Vio;
 

=== modified file 'libmysql/errmsg.c'
--- a/libmysql/errmsg.c	2007-04-12 22:56:22 +0000
+++ b/libmysql/errmsg.c	2008-05-20 16:36:26 +0000
@@ -84,6 +84,7 @@
   "This feature is not implemented yet",
   "Lost connection to MySQL server at '%s', system error: %d",
   "Statement closed indirectly because of a preceeding %s() call",
+  "The number of columns in the result set differs from the number of bound buffers. You
must reset the statement, rebind the result set columns, and execute the statement
again",
   ""
 };
 
@@ -149,6 +150,7 @@
   "This feature is not implemented yet",
   "Lost connection to MySQL server at '%s', system error: %d",
   "Statement closed indirectly because of a preceeding %s() call",
+  "The number of columns in the result set differs from the number of bound buffers. You
must reset the statement, rebind the result set columns, and execute the statement
again",
   ""
 };
 
@@ -212,6 +214,7 @@
   "This feature is not implemented yet",
   "Lost connection to MySQL server at '%s', system error: %d",
   "Statement closed indirectly because of a preceeding %s() call",
+  "The number of columns in the result set differs from the number of bound buffers. You
must reset the statement, rebind the result set columns, and execute the statement
again",
   ""
 };
 #endif

=== modified file 'libmysql/libmysql.c'
--- a/libmysql/libmysql.c	2008-05-08 16:01:15 +0000
+++ b/libmysql/libmysql.c	2008-05-21 10:17:29 +0000
@@ -1335,6 +1335,7 @@
 #define RESET_SERVER_SIDE 1
 #define RESET_LONG_DATA 2
 #define RESET_STORE_RESULT 4
+#define RESET_CLEAR_ERROR 8
 
 static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags);
 
@@ -1718,7 +1719,7 @@
   To be removed when all commands will fully support prepared mode.
 */
 
-static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
+static void alloc_stmt_fields(MYSQL_STMT *stmt)
 {
   MYSQL_FIELD *fields, *field, *end;
   MEM_ROOT *alloc= &stmt->mem_root;
@@ -1736,7 +1737,10 @@
       !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc,
 					      sizeof(MYSQL_BIND) *
 					      stmt->field_count)))
-    return 0;
+  {
+    set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
+    return;
+  }
 
   for (fields= mysql->fields, end= fields+stmt->field_count,
 	 field= stmt->fields;
@@ -1755,13 +1759,15 @@
     field->def      = fields->def ? strdup_root(alloc,fields->def): 0;
     field->max_length= 0;
   }
-  return stmt->field_count;
 }
 
 
-/*
+/**
   Update result set columns metadata if it was sent again in
   reply to COM_STMT_EXECUTE.
+
+  @note If the new field count is different from the original one,
+        an error is set and no update is performed.
 */
 
 static void update_stmt_fields(MYSQL_STMT *stmt)
@@ -1771,7 +1777,22 @@
   MYSQL_FIELD *stmt_field= stmt->fields;
   MYSQL_BIND *my_bind= stmt->bind_result_done ? stmt->bind : 0;
 
-  DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count);
+  if (stmt->field_count != stmt->mysql->field_count)
+  {
+    /*
+      The tables used in the statement were altered,
+      and the query now returns a different number of columns.
+      There is no way to continue without reallocating the bind
+      array:
+      - if the number of columns increased, mysql_stmt_fetch()
+      will write beyond allocated memory
+      - if the number of columns decreased, some user-bound
+      buffers will be left unassigned without user knowing
+      that.
+    */
+    set_stmt_error(stmt, CR_NEW_STMT_METADATA, unknown_sqlstate, NULL);
+    return;
+  }
 
   for (; field < field_end; ++field, ++stmt_field)
   {
@@ -2419,6 +2440,50 @@
 }
 
 
+/**
+  Update statement result set metadata from with the new field
+  information sent during statement execute.
+
+  @pre mysql->field_count is not zero
+
+  @retval TRUE   if error: out of memory or the new
+                 result set has a different number of columns
+  @retval FALSE  success
+*/
+
+static void reinit_result_set_metadata(MYSQL_STMT *stmt)
+{
+  /* Server has sent result set metadata */
+  if (stmt->field_count == 0)
+  {
+    /*
+      This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
+      prepared statements can't send result set metadata for these queries
+      on prepare stage. Read it now.
+    */
+    alloc_stmt_fields(stmt);
+  }
+  else
+  {
+    /*
+      Update result set metadata if it for some reason changed between
+      prepare and execute, i.e.:
+      - in case of 'SELECT ?' we don't know column type unless data was
+      supplied to mysql_stmt_execute, so updated column type is sent
+      now.
+      - if data dictionary changed between prepare and execute, for
+      example a table used in the query was altered.
+      Note, that now (4.1.3) we always send metadata in reply to
+      COM_STMT_EXECUTE (even if it is not necessary), so either this or
+      previous branch always works.
+      TODO: send metadata only when it's really necessary and add a warning
+      'Metadata changed' when it's sent twice.
+      */
+    update_stmt_fields(stmt);
+  }
+}
+
+
 /*
   Send placeholders data to server (if there are placeholders)
   and execute prepared statement.
@@ -2474,7 +2539,7 @@
     DBUG_RETURN(1);
   }
 
-  if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
+  if (reset_stmt_handle(stmt, RESET_STORE_RESULT | RESET_CLEAR_ERROR))
     DBUG_RETURN(1);
   /*
     No need to check for stmt->state: if the statement wasn't
@@ -2482,40 +2547,10 @@
   */
   if (mysql->methods->stmt_execute(stmt))
     DBUG_RETURN(1);
+  stmt->state= MYSQL_STMT_EXECUTE_DONE;
   if (mysql->field_count)
   {
-    /* Server has sent result set metadata */
-    if (stmt->field_count == 0)
-    {
-      /*
-        This is 'SHOW'/'EXPLAIN'-like query. Current implementation of
-        prepared statements can't send result set metadata for these queries
-        on prepare stage. Read it now.
-      */
-      alloc_stmt_fields(stmt);
-    }
-    else
-    {
-      /*
-        Update result set metadata if it for some reason changed between
-        prepare and execute, i.e.:
-        - in case of 'SELECT ?' we don't know column type unless data was
-          supplied to mysql_stmt_execute, so updated column type is sent
-          now.
-        - if data dictionary changed between prepare and execute, for
-          example a table used in the query was altered.
-        Note, that now (4.1.3) we always send metadata in reply to
-        COM_STMT_EXECUTE (even if it is not necessary), so either this or
-        previous branch always works.
-        TODO: send metadata only when it's really necessary and add a warning
-        'Metadata changed' when it's sent twice.
-      */
-      update_stmt_fields(stmt);
-    }
-  }
-  stmt->state= MYSQL_STMT_EXECUTE_DONE;
-  if (stmt->field_count)
-  {
+    reinit_result_set_metadata(stmt);
     if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
     {
       mysql->status= MYSQL_STATUS_READY;
@@ -2530,7 +2565,7 @@
         network or b) is more efficient if all (few) result set rows are
         precached on client and server's resources are freed.
       */
-      DBUG_RETURN(mysql_stmt_store_result(stmt));
+      mysql_stmt_store_result(stmt);
     }
     else
     {
@@ -2539,7 +2574,7 @@
       stmt->read_row_func= stmt_read_row_unbuffered;
     }
   }
-  DBUG_RETURN(0);
+  DBUG_RETURN(test(stmt->last_errno));
 }
 
 
@@ -4373,6 +4408,12 @@
     DBUG_RETURN(1);
   }
 
+  if (stmt->last_errno)
+  {
+    /* An attempt to use an invalid statement handle. */
+    DBUG_RETURN(1);
+  }
+
   if (mysql->status == MYSQL_STATUS_READY &&
       stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
   {
@@ -4580,9 +4621,10 @@
           stmt->state= MYSQL_STMT_INIT_DONE;
           return 1;
         }
-        stmt_clear_error(stmt);
       }
     }
+    if (flags & RESET_CLEAR_ERROR)
+      stmt_clear_error(stmt);
     stmt->state= MYSQL_STMT_PREPARE_DONE;
   }
   return 0;
@@ -4593,7 +4635,8 @@
   DBUG_ENTER("mysql_stmt_free_result");
 
   /* Free the client side and close the server side cursor if there is one */
-  DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT));
+  DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT |
+                                RESET_CLEAR_ERROR));
 }
 
 /********************************************************************
@@ -4674,7 +4717,9 @@
     DBUG_RETURN(1);
   }
   /* Reset the client and server sides of the prepared statement */
-  DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA));
+  DBUG_RETURN(reset_stmt_handle(stmt,
+                                RESET_SERVER_SIDE | RESET_LONG_DATA |
+                                RESET_CLEAR_ERROR));
 }
 
 /*

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2008-05-12 11:11:22 +0000
+++ b/libmysqld/lib_sql.cc	2008-05-21 10:17:29 +0000
@@ -285,7 +285,6 @@
   my_bool res;
 
   int4store(header, stmt->stmt_id);
-  header[4]= (uchar)stmt->flags;
   thd= (THD*)stmt->mysql->thd;
   thd->client_param_count= stmt->param_count;
   thd->client_params= stmt->params;

=== modified file 'mysql-test/r/locktrans_innodb.result'
--- a/mysql-test/r/locktrans_innodb.result	2008-04-02 09:33:21 +0000
+++ b/mysql-test/r/locktrans_innodb.result	2008-05-21 19:44:56 +0000
@@ -94,12 +94,12 @@
 # Implicit lock method conversion due to mix in statement.
 LOCK TABLE t1 READ, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 # Lock t1 share (converted to read), t2 write.
 LOCK TABLE t1 IN SHARE MODE, t2 WRITE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't1'
 # Show t1 is read locked, t2 write locked.
 INSERT INTO t1 SELECT * FROM t2;
 ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
@@ -110,8 +110,8 @@
 # Lock t1 exclusive (converted to write), t2 share (converted to read).
 LOCK TABLE t1 IN EXCLUSIVE MODE, t2 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 # Show t1 is write locked, t2 read locked.
 INSERT INTO t1 SELECT * FROM t2;
 INSERT INTO t2 SELECT * FROM t1;
@@ -135,8 +135,8 @@
 ## Error is reported on first table only. Show both errors:
 SHOW WARNINGS;
 Level	Code	Message
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't1'
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't2'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't1'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't2'
 UNLOCK TABLES;
 SET @@SQL_MODE= @wl3561_save_sql_mode;
 #
@@ -156,7 +156,7 @@
 # Request a transactional lock, which is converted to non-transactional.
 LOCK TABLE t4 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Try a conflict with the existing non-transactional lock.
 INSERT INTO t4 VALUES(444);
 ERROR HY000: Table 't4' was locked with a READ lock and can't be updated
@@ -173,8 +173,8 @@
 # Request a share lock on the view, which is converted to read locks.
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Show that read locks on the base tables prohibit writing ...
 INSERT INTO t3 SELECT * FROM t4;
 ERROR HY000: Table 't3' was locked with a READ lock and can't be updated
@@ -190,7 +190,7 @@
 ## Report conversion on view due to existing non-transactional locks.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);
@@ -199,8 +199,8 @@
 ## Now report conversion on base table again.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);

=== modified file 'mysql-test/r/locktrans_myisam.result'
--- a/mysql-test/r/locktrans_myisam.result	2008-04-14 10:15:04 +0000
+++ b/mysql-test/r/locktrans_myisam.result	2008-05-21 19:44:56 +0000
@@ -21,8 +21,8 @@
 # Valid syntax for transactional locks.
 LOCK TABLE t1 IN SHARE MODE, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 #
 # Valid syntax for aliases with and without 'AS'.
@@ -30,19 +30,19 @@
 UNLOCK TABLES;
 LOCK TABLE t1 AS a1 IN SHARE MODE, t2 a2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'a1'
-Warning	1613	Converted to non-transactional lock on 'a2'
+Warning	1614	Converted to non-transactional lock on 'a1'
+Warning	1614	Converted to non-transactional lock on 'a2'
 UNLOCK TABLES;
 #
 # Transactional locks taken on a view.
 CREATE VIEW v1 AS SELECT * FROM t1, t2 WHERE t1.c1 = t2.c2;
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 DROP VIEW v1;
 #
 # Locking INFORMATION_SCHEMA fails on missing privileges.
@@ -94,12 +94,12 @@
 # Implicit lock method conversion due to mix in statement.
 LOCK TABLE t1 READ, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 # Lock t1 share (converted to read), t2 write.
 LOCK TABLE t1 IN SHARE MODE, t2 WRITE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't1'
 # Show t1 is read locked, t2 write locked.
 INSERT INTO t1 SELECT * FROM t2;
 ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
@@ -110,8 +110,8 @@
 # Lock t1 exclusive (converted to write), t2 share (converted to read).
 LOCK TABLE t1 IN EXCLUSIVE MODE, t2 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 # Show t1 is write locked, t2 read locked.
 INSERT INTO t1 SELECT * FROM t2;
 INSERT INTO t2 SELECT * FROM t1;
@@ -135,8 +135,8 @@
 ## Error is reported on first table only. Show both errors:
 SHOW WARNINGS;
 Level	Code	Message
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't1'
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't2'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't1'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't2'
 UNLOCK TABLES;
 SET @@SQL_MODE= @wl3561_save_sql_mode;
 #
@@ -156,7 +156,7 @@
 # Request a transactional lock, which is converted to non-transactional.
 LOCK TABLE t4 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Try a conflict with the existing non-transactional lock.
 INSERT INTO t4 VALUES(444);
 ERROR HY000: Table 't4' was locked with a READ lock and can't be updated
@@ -173,8 +173,8 @@
 # Request a share lock on the view, which is converted to read locks.
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Show that read locks on the base tables prohibit writing ...
 INSERT INTO t3 SELECT * FROM t4;
 ERROR HY000: Table 't3' was locked with a READ lock and can't be updated
@@ -190,7 +190,7 @@
 ## Report conversion on view due to existing non-transactional locks.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);
@@ -199,8 +199,8 @@
 ## Now report conversion on base table again.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);

=== modified file 'mysql-test/r/log_output_basic.result'
--- a/mysql-test/r/log_output_basic.result	2008-04-10 13:14:28 +0000
+++ b/mysql-test/r/log_output_basic.result	2008-05-20 18:23:58 +0000
@@ -15,9 +15,10 @@
 SET @@global.log_output = NULL;
 ERROR 42000: Variable 'log_output' can't be set to the value of 'NULL'
 SET @@global.log_output = "";
+ERROR 42000: Variable 'log_output' can't be set to the value of ''
 SELECT @@global.log_output;
 @@global.log_output
-
+FILE
 'Bug# 34838: Empty value is allowed where as it is not specified in';
 'documentation';
 '#--------------------FN_DYNVARS_065_03------------------------#'
@@ -117,6 +118,7 @@
 1
 '#---------------------FN_DYNVARS_065_07-------------------------#'
 SET @@global.log_output = 0;
+ERROR 42000: Variable 'log_output' can't be set to the value of '0'
 SELECT @@global.log_output;
 @@global.log_output
 
@@ -156,9 +158,10 @@
 @@global.log_output
 NONE
 SET @@global.log_output = FALSE;
+ERROR 42000: Variable 'log_output' can't be set to the value of '0'
 SELECT @@global.log_output;
 @@global.log_output
-
+NONE
 '#---------------------FN_DYNVARS_065_09----------------------#'
 SET log_output = ON;
 ERROR HY000: Variable 'log_output' is a GLOBAL variable and should be set with SET GLOBAL

=== modified file 'mysql-test/r/ps.result'
--- a/mysql-test/r/ps.result	2008-03-05 10:32:49 +0000
+++ b/mysql-test/r/ps.result	2008-05-21 10:17:29 +0000
@@ -2902,4 +2902,22 @@
 ERROR 21000: Subquery returns more than 1 row
 deallocate prepare stmt;
 drop table t1, t2;
+#
+# Bug#27430 Crash in subquery code when in PS and table DDL changed
+# after PREPARE
+#
+# This part of the test doesn't work in embedded server, this is
+# why it's here. For the main test see ps_ddl*.test
+
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show events where (1) in (select * from t1)";
+execute stmt;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status	Originator	character_set_client	collation_connection	Database
Collation
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval
field	Starts	Ends	Status	Originator	character_set_client	collation_connection	Database
Collation
+drop table t1;
+deallocate prepare stmt;
 End of 5.1 tests.

=== modified file 'mysql-test/r/ps_1general.result'
--- a/mysql-test/r/ps_1general.result	2007-12-13 12:55:04 +0000
+++ b/mysql-test/r/ps_1general.result	2008-05-21 10:17:29 +0000
@@ -143,32 +143,32 @@
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+a	c	b
+9	9	recreated table
 drop table t5 ;
 create table t5
 (
 a int primary key,
 b char(30),
 c int,
-d timestamp default current_timestamp
+d timestamp default '2008-02-23 09:23:45'
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+a	b	c	d
+9	recreated table	9	2008-02-23 09:23:45
 drop table t5 ;
 create table t5
 (
 a int primary key,
-d timestamp default current_timestamp,
+d timestamp default '2008-02-23 09:23:45',
 b char(30),
 c int
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
-a	b	c
-9	recreated table	9
+a	d	b	c
+9	2008-02-23 09:23:45	recreated table	9
 drop table t5 ;
 create table t5
 (
@@ -189,7 +189,8 @@
 );
 insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
 execute stmt2 ;
-ERROR 42S22: Unknown column 'test.t5.a' in 'field list'
+f1	f2	f3
+9	recreated table	9
 drop table t5 ;
 prepare stmt1 from ' select * from t1 where a <= 2 ' ;
 execute stmt1 ;

=== modified file 'mysql-test/r/ps_ddl.result'
--- a/mysql-test/r/ps_ddl.result	2008-04-07 09:35:42 +0000
+++ b/mysql-test/r/ps_ddl.result	2008-05-21 19:44:56 +0000
@@ -35,6 +35,7 @@
 =====================================================================
 Part 4: TABLE -> NOTHING transitions
 =====================================================================
+# Test 4-a: select ... from <table>
 create table t1 (a int);
 prepare stmt from "select * from t1";
 execute stmt;
@@ -59,6 +60,32 @@
 SUCCESS
 
 deallocate prepare stmt;
+# Test 4-b: TABLE -> NOTHING by renaming the table
+create table t1 (a int);
+prepare stmt from "select * from t1";
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+rename table t1 to t2;
+execute stmt;
+ERROR 42S02: Table 'test.t1' doesn't exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+ERROR 42S02: Table 'test.t1' doesn't exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt;
+drop table t2;
 =====================================================================
 Part 5: TABLE -> TABLE (DDL) transitions
 =====================================================================
@@ -192,6 +219,7 @@
 select @message;
 @message
 new trigger: 11
+Test 6-e: removing a relevant trigger
 drop trigger t1_bi;
 set @val=12;
 execute stmt using @val;
@@ -338,7 +366,7 @@
 #
 # Sic: the insert went into t3, even though the view now
 # points at t2. This is because neither the merged view
-#  nor its prelocking list are affected by view DDL 
+#  nor its prelocking list are affected by view DDL
 # The binary log is of course wrong, since it is not
 # using prepared statements
 #
@@ -380,7 +408,7 @@
 # Since the dependent table is tracked in the prelocked
 # list of the prepared statement, invalidation happens
 # and the statement is re-prepared. This is an unnecessary
-# side effect, since the statement that *is* dependent 
+# side effect, since the statement that *is* dependent
 # on t2 definition is inside the trigger, and it is currently
 # not reprepared (see the previous test case).
 execute stmt using @var;
@@ -398,7 +426,7 @@
 drop table t1,t2;
 # Test 7-e: dependent TABLE TRIGGER has changed
 create table t1 (a int);
-create trigger t1_ai after insert on t1 for each row 
+create trigger t1_ai after insert on t1 for each row
 insert into t2 (a) values (new.a);
 create table t2 (a int unique);
 create trigger t2_ai after insert on t2 for each row
@@ -444,6 +472,7 @@
 =====================================================================
 Part 8: TABLE -> TEMPORARY TABLE transitions
 =====================================================================
+# Test 8-a: base table used recreated as temporary table
 create table t1 (a int);
 prepare stmt from "select * from t1";
 execute stmt;
@@ -462,6 +491,37 @@
 
 drop table t1;
 deallocate prepare stmt;
+# Test 8-b: temporary table has precedence over base table with same name
+create table t1 (a int);
+prepare stmt from 'select count(*) from t1';
+execute stmt;
+count(*)
+0
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+count(*)
+0
+call p_verify_reprepare_count(0);
+SUCCESS
+
+create temporary table t1 AS SELECT 1;
+execute stmt;
+count(*)
+1
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+count(*)
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt;
+drop temporary table t1;
+drop table t1;
 =====================================================================
 Part 9: TABLE -> VIEW transitions
 =====================================================================
@@ -503,6 +563,7 @@
 =====================================================================
 Part 11: TEMPORARY TABLE -> TABLE transitions
 =====================================================================
+# Test 11-a: temporary table replaced by base table
 create table t1 (a int);
 insert into t1 (a) value (1);
 create temporary table t1 (a int);
@@ -524,6 +585,38 @@
 1
 drop table t1;
 deallocate prepare stmt;
+# Test 11-b: temporary table has precedence over base table with same name
+#            temporary table disappears
+create table t1 (a int);
+create temporary table t1 as select 1 as a;
+prepare stmt from "select count(*) from t1";
+execute stmt;
+count(*)
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+count(*)
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop temporary table t1;
+execute stmt;
+count(*)
+0
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+count(*)
+0
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt;
+drop table t1;
 =====================================================================
 Part 12: TEMPORARY TABLE -> TEMPORARY TABLE (DDL) transitions
 =====================================================================
@@ -663,6 +756,8 @@
 # Currently a different result from conventional statements.
 # A view is inlined once at prepare, later on view DDL
 # does not affect prepared statement and it is not re-prepared.
+# This is reported in Bug#36002 Prepared statements: if a view
+# used in a statement is replaced, bad data
 execute stmt;
 a	b	c
 10	20	30
@@ -738,6 +833,7 @@
 create procedure p1(out x int) select max(a) from t2 into x;
 # XXX: bug. The prelocked list is not invalidated
 # and we keep opening table t1, whereas the procedure
+# is now referring to table t2
 execute stmt;
 ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or
definer/invoker of view lack rights to use them
 call p_verify_reprepare_count(0);
@@ -1005,7 +1101,7 @@
 execute stmt;
 drop table t1;
 create table t1 (a1 int, a2 int);
-# t1 has changed, and it's does not lead to reprepare 
+# t1 has changed, and it's does not lead to reprepare
 execute stmt;
 alter table t1 drop column b;
 execute stmt;
@@ -1028,7 +1124,7 @@
 drop table t1;
 create table t1 (a1 int, a2 int);
 insert into t1 values (1, 10), (2, 20), (3, 30);
-# t1 has changed, and it's does not lead to reprepare 
+# t1 has changed, and it's does not lead to reprepare
 execute stmt;
 Table	Op	Msg_type	Msg_text
 test.t1	repair	status	OK
@@ -1331,7 +1427,7 @@
 (2, 2),
 (1234, 3),
 (1234, 4);
-prepare stmt from 
+prepare stmt from
 "select oref, a, a in (select a from t_27430_1 where oref=t_27430_2.oref) Z from
t_27430_2";
 execute stmt;
 oref	a	Z
@@ -1344,7 +1440,7 @@
 
 drop table t_27430_1, t_27430_2;
 create table t_27430_1 (a int, oref int, key(a));
-insert into t_27430_1 values 
+insert into t_27430_1 values
 (1, 1),
 (1, NULL),
 (2, 3),
@@ -1497,8 +1593,597 @@
 drop procedure p_12093;
 deallocate prepare stmt_sf;
 deallocate prepare stmt_sp;
+=====================================================================
+Ensure that metadata validation is performed for every type of
+SQL statement where it is needed.
+=====================================================================
+#
+# SQLCOM_SELECT
+#
+#
+# SQLCOM_CREATE_TABLE
+#
+drop table if exists t1;
+drop table if exists t2;
+create table t1 (a int);
+prepare stmt from 'create table t2 as select * from t1';
+execute stmt;
+drop table t2;
+execute stmt;
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+ERROR 42S01: Table 't2' already exists
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+ERROR 42S01: Table 't2' already exists
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+create temporary table t2 (a int);
+execute stmt;
+ERROR 42S01: Table 't2' already exists
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+ERROR 42S01: Table 't2' already exists
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop temporary table t2;
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+create view t2 as select 1;
+execute stmt;
+Got one of the listed errors
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+Got one of the listed errors
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop view t2;
+drop table t1;
+create table t1 (x varchar(20));
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+select * from t2;
+x
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+alter table t1 add column y decimal(10,3);
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+select * from t2;
+x	y
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+deallocate prepare stmt;
+# XXX: no validation of the first table in case of
+# CREATE TEMPORARY TABLE. This is a shortcoming of the current code,
+# but since validation is not strictly necessary, nothing is done
+# about it.
+# Will be fixed as part of work on Bug#21431 "Incomplete support of
+# temporary tables"
+create table t1 (a int);
+insert into t1 (a) values (1);
+prepare stmt from "create temporary table if not exists t2 as select * from t1";
+execute stmt;
+drop table t2;
+execute stmt;
+execute stmt;
+Warnings:
+Note	1050	Table 't2' already exists
+select * from t2;
+a
+1
+1
+execute stmt;
+Warnings:
+Note	1050	Table 't2' already exists
+select * from t2;
+a
+1
+1
+1
+drop table t2;
+create temporary table t2 (a varchar(10));
+execute stmt;
+Warnings:
+Note	1050	Table 't2' already exists
+select * from t2;
+a
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Warnings:
+Note	1050	Table 't2' already exists
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+Warnings:
+Note	1050	Table 't2' already exists
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+drop temporary table t2;
+drop table t2;
+deallocate prepare stmt;
+create table t1 (a int);
+prepare stmt from "create table t2 like t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+drop table t1;
+execute stmt;
+ERROR 42S02: Table 'test.t1' doesn't exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+ERROR 42S02: Table 'test.t1' doesn't exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+create table t1 (x char(17));
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t2;
+alter table t1 add column y time;
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+select * from t2;
+x	y
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+drop table t2;
+deallocate prepare stmt;
+#
+# SQLCOM_UPDATE
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "update t2 set a=a+1 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_INSERT
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "insert into t2 set a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_INSERT_SELECT
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "insert into t2 select * from t1";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_REPLACE
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "replace t2 set a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_REPLACE_SELECT
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "replace t2 select * from t1";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_DELETE
+#
+drop table if exists t1, t2;
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "delete from t2 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+#
+# SQLCOM_DELETE_MULTI
+#
+drop table if exists t1, t2, t3;
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+prepare stmt from "delete t2, t3 from t2, t3 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2, t3;
+deallocate prepare stmt;
+#
+# SQLCOM_UPDATE_MULTI
+#
+drop table if exists t1, t2, t3;
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+prepare stmt from "update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from
t1)";
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2, t3;
+deallocate prepare stmt;
+# Intermediate results: 8 SQLCOMs tested, 8 automatic reprepares
+call p_verify_reprepare_count(8);
+SUCCESS
+
+#
+# SQLCOM_LOAD
+#
+drop table if exists t1;
+create table t1 (a varchar(20));
+prepare stmt from "load data infile '../std_data_ln/words.dat' into table t1";
+ERROR HY000: This command is not supported in the prepared statement protocol yet
+drop table t1;
+#
+# SQLCOM_SHOW_DATABASES
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show databases where (1) in (select * from t1)";
+execute stmt;
+Database
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Database
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_TABLES
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show tables where (1) in (select * from t1)";
+execute stmt;
+Tables_in_test
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Tables_in_test
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_FIELDS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show fields from t1 where (1) in (select * from t1)";
+execute stmt;
+Field	Type	Null	Key	Default	Extra
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Field	Type	Null	Key	Default	Extra
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_KEYS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show keys from t1 where (1) in (select * from t1)";
+execute stmt;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_Comment
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_Comment
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_VARIABLES
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show variables where (1) in (select * from t1)";
+execute stmt;
+Variable_name	Value
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Variable_name	Value
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_STATUS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show status where (1) in (select * from t1)";
+execute stmt;
+Variable_name	Value
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Variable_name	Value
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_LOGS,
+# SQLCOM_SHOW_ENGINE_MUTEX, SQLCOM_SHOW_PROCESSLIST
+#
+# Currently can not have a where clause, need to be covered
+# with tests
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show engine all status where (1) in (select * from t1)";
+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 'where (1) in (select * from
t1)' at line 1
+prepare stmt from "show engine all logs where (1) in (select * from t1)";
+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 'where (1) in (select * from
t1)' at line 1
+prepare stmt from "show engine all mutex where (1) in (select * from t1)";
+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 'where (1) in (select * from
t1)' at line 1
+prepare stmt from "show processlist where (1) in (select * from t1)";
+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 'where (1) in (select * from
t1)' at line 1
+drop table t1;
+#
+# SQLCOM_SHOW_CHARSETS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show charset where (1) in (select * from t1)";
+execute stmt;
+Charset	Description	Default collation	Maxlen
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Charset	Description	Default collation	Maxlen
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_COLLATIONS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show collation where (1) in (select * from t1)";
+execute stmt;
+Collation	Charset	Id	Default	Compiled	Sortlen
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Collation	Charset	Id	Default	Compiled	Sortlen
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_TABLE_STATUS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show table status where (1) in (select * from t1)";
+execute stmt;
+Name	Engine	Version	Row_format	Rows	Avg_row_length	Data_length	Max_data_length	Index_length	Data_free	Auto_increment	Create_time	Update_time	Check_time	Collation	Checksum	Create_options	Comment
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Name	Engine	Version	Row_format	Rows	Avg_row_length	Data_length	Max_data_length	Index_length	Data_free	Auto_increment	Create_time	Update_time	Check_time	Collation	Checksum	Create_options	Comment
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_TRIGGERS
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show triggers where (1) in (select * from t1)";
+execute stmt;
+Trigger	Event	Table	Statement	Timing	Created	sql_mode	Definer	character_set_client	collation_connection	Database
Collation
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Trigger	Event	Table	Statement	Timing	Created	sql_mode	Definer	character_set_client	collation_connection	Database
Collation
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_OPEN_TABLES
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show open tables where (1) in (select * from t1)";
+execute stmt;
+Database	Table	In_use	Name_locked
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Database	Table	In_use	Name_locked
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_STATUS_PROC
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show procedure status where (1) in (select * from t1)";
+execute stmt;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database
Collation
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database
Collation
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_STATUS_FUNC
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "show function status where (1) in (select * from t1)";
+execute stmt;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database
Collation
+drop table t1;
+create table t1 (x int);
+execute stmt;
+Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database
Collation
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_SHOW_EVENTS
+#
+#
+# Please see this test in ps.test, it requires not_embedded.inc
+#
+#
+# SQLCOM_SET_OPTION
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "set @a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_DO
+#
+drop table if exists t1;
+create table t1 (a int);
+prepare stmt from "do ((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+#
+# SQLCOM_CALL
+#
+drop table if exists t1;
+drop procedure if exists p1;
+create procedure p1(a int) begin end;
+create table t1 (a int);
+prepare stmt from "call p1((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+drop procedure p1;
+deallocate prepare stmt;
+#
+# SQLCOM_CREATE_VIEW
+#
+drop table if exists t1;
+drop view if exists v1;
+create table t1 (a int);
+prepare stmt from "create view v1 as select * from t1";
+execute stmt;
+drop view v1;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop view v1;
+drop table t1;
+deallocate prepare stmt;
+# Intermediate result: number of reprepares matches the number
+# of tests
+call p_verify_reprepare_count(17);
+SUCCESS
+
+#
+# SQLCOM_ALTER_VIEW
+#
+drop view if exists v1;
+create view v1 as select 1;
+prepare stmt from "alter view v1 as select 2";
+ERROR HY000: This command is not supported in the prepared statement protocol yet
+drop view v1;
 # Cleanup
-# 
+#
 drop temporary table if exists t1, t2, t3;
 drop table if exists t1, t2, t3, v1, v2;
 drop procedure if exists p_verify_reprepare_count;

=== added file 'mysql-test/r/ps_ddl1.result'
--- a/mysql-test/r/ps_ddl1.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/ps_ddl1.result	2008-04-18 19:18:53 +0000
@@ -0,0 +1,482 @@
+drop temporary table if exists t1;
+drop table if exists t1, t2;
+drop procedure if exists p_verify_reprepare_count;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists t1;
+drop schema if exists mysqltest;
+create procedure p_verify_reprepare_count(expected int)
+begin
+declare old_reprepare_count int default @reprepare_count;
+select variable_value from
+information_schema.session_status where
+variable_name='com_stmt_reprepare'
+  into @reprepare_count;
+if old_reprepare_count + expected <> @reprepare_count then
+select concat("Expected: ", expected,
+", actual: ", @reprepare_count - old_reprepare_count)
+as "ERROR";
+else
+select '' as "SUCCESS";
+end if;
+end|
+set @reprepare_count= 0;
+flush status;
+drop table if exists t1;
+# Column added or dropped is not within the list of selected columns
+# or table comment has changed.
+# A reprepare is probably not needed.
+create table t1 (a int, b int);
+prepare stmt from "select a from t1";
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+alter table t1 add column c int;
+execute stmt;
+a
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+alter table t1 drop column b;
+execute stmt;
+a
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+alter table t1 comment "My best table";
+execute stmt;
+a
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+a
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+deallocate prepare stmt;
+# Selects using the table at various positions, inser,update ...
+# + the table disappears
+create table t1 (a int);
+prepare stmt1 from "truncate t1";
+prepare stmt2 from "select 1 as my_column from t1";
+prepare stmt3 from "select 1 as my_column from (select * from t1) as t2";
+prepare stmt4 from
+"select 1 as my_column from (select 1) as t2 where exists (select 1 from t1)";
+prepare stmt5 from "select * from (select 1 as b) as t2, t1";
+prepare stmt6 from "select * from t1 union all select 1.5";
+prepare stmt7 from "select 1 as my_column union all select 1 from t1";
+prepare stmt8 from "insert into t1 values(1),(2)";
+prepare stmt9 from "update t1 set a = 3 where a = 2";
+prepare stmt10 from "delete from t1 where a = 1";
+# Attention: Result logging is disabled.
+execute stmt10;
+execute stmt9;
+execute stmt8;
+execute stmt7;
+execute stmt6;
+execute stmt5;
+execute stmt4;
+execute stmt3;
+execute stmt2;
+execute stmt1;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+execute stmt10;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt9;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt8;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt7;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt6;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt5;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt4;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt3;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt2;
+ERROR 42S02: Table 'test.t1' doesn't exist
+execute stmt1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt10;
+deallocate prepare stmt9;
+deallocate prepare stmt8;
+deallocate prepare stmt7;
+deallocate prepare stmt6;
+deallocate prepare stmt5;
+deallocate prepare stmt4;
+deallocate prepare stmt3;
+deallocate prepare stmt2;
+deallocate prepare stmt1;
+# Selects using the table at various positions, inser,update ...
+# + layout change (drop column) which must cause a reprepare
+create table t1 (a int, b int);
+insert into t1 values(1,1),(2,2),(3,3);
+create table t2 like t1;
+insert into t1 values(2,2);
+prepare stmt1 from "select a,b from t1";
+prepare stmt2 from "select a,b from (select * from t1) as t1";
+prepare stmt3 from "select * from t1 where a = 2 and b = 2";
+prepare stmt4 from "select * from t2 where (a,b) in (select * from t1)";
+prepare stmt5 from "select * from t1 union select * from t2";
+prepare stmt6 from "select * from t1 union all select * from t2";
+prepare stmt7 from "insert into t1 set a = 4, b = 4";
+prepare stmt8 from "insert into t1 select * from t2";
+# Attention: Result logging is disabled.
+execute stmt8;
+execute stmt7;
+execute stmt6;
+execute stmt5;
+execute stmt4;
+execute stmt3;
+execute stmt2;
+execute stmt1;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+alter table t1 drop column b;
+execute stmt8;
+ERROR 21S01: Column count doesn't match value count at row 1
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt7;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt6;
+ERROR 21000: The used SELECT statements have a different number of columns
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt5;
+ERROR 21000: The used SELECT statements have a different number of columns
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt4;
+ERROR 21000: Operand should contain 2 column(s)
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt3;
+ERROR 42S22: Unknown column 'b' in 'where clause'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt2;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt1;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt8;
+ERROR 21S01: Column count doesn't match value count at row 1
+call p_verify_reprepare_count(1);
+ERROR
+Expected: 1, actual: 0
+execute stmt7;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt6;
+ERROR 21000: The used SELECT statements have a different number of columns
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt5;
+ERROR 21000: The used SELECT statements have a different number of columns
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt4;
+ERROR 21000: Operand should contain 2 column(s)
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt3;
+ERROR 42S22: Unknown column 'b' in 'where clause'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt2;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt1;
+ERROR 42S22: Unknown column 'b' in 'field list'
+call p_verify_reprepare_count(1);
+SUCCESS
+
+# Why does the INSERT ... SELECT does not get a reprepare or is
+# only the counter not incremented?
+execute stmt8;
+ERROR 21S01: Column count doesn't match value count at row 1
+call p_verify_reprepare_count(1);
+ERROR
+Expected: 1, actual: 0
+alter table t2 add column c int;
+execute stmt8;
+ERROR 21S01: Column count doesn't match value count at row 1
+call p_verify_reprepare_count(1);
+SUCCESS
+
+deallocate prepare stmt8;
+deallocate prepare stmt7;
+deallocate prepare stmt6;
+deallocate prepare stmt5;
+deallocate prepare stmt4;
+deallocate prepare stmt3;
+deallocate prepare stmt2;
+deallocate prepare stmt1;
+drop table t1;
+drop table t2;
+# select AVG(<col>) + optimizer uses index meets loss of the index
+create table t1 (a int, b int, primary key(b),unique index t1_unq_idx(a));
+insert into t1 set a = 0, b = 0;
+insert into t1 select a + 1, b + 1 from t1;
+insert into t1 select a + 2, b + 2 from t1;
+insert into t1 select a + 4, b + 4 from t1;
+insert into t1 select a + 8, b + 8 from t1;
+# Optimizer strategy: Possible keys = NULL , Extra = Using index
+prepare stmt from "select avg(a) from t1";
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(0);
+SUCCESS
+
+alter table t1 drop index t1_unq_idx;
+# Optimizer strategy: Possible keys = NULL , Extra = 
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(0);
+SUCCESS
+
+# select AVG(<col>) + optimizer uses table scan meets a new index
+alter table t1 add unique index t1_unq_idx(a);
+# Optimizer strategy: Possible keys = NULL , Extra = Using index
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+avg(a)
+7.5000
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt;
+drop table t1;
+# table replaced by not updatable view - Insert
+create table t1 (a int);
+prepare stmt from "insert into t1 values(1)";
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop table t1;
+create view t1 as select 1;
+execute stmt;
+ERROR HY000: The target table t1 of the INSERT is not insertable-into
+call p_verify_reprepare_count(1);
+SUCCESS
+
+drop view t1;
+create table t2 (a int);
+create view t1 as select * from t2 with check option;
+execute stmt;
+call p_verify_reprepare_count(1);
+SUCCESS
+
+execute stmt;
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+1
+deallocate prepare stmt;
+drop view t1;
+drop table t2;
+=====================================================================
+Some freestyle tests
+=====================================================================
+create temporary table t1 as select 1 as a;
+create procedure p1()
+begin
+drop temporary table t1;
+end|
+create function f1() returns int
+begin
+call p1();
+return 1;
+end|
+prepare stmt from "select f1() as my_column, a from t1";
+execute stmt;
+ERROR HY000: Can't reopen table: 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+prepare stmt from "select a, f1() as my_column from t1";
+execute stmt;
+ERROR HY000: Can't reopen table: 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+prepare stmt from "select f1() as my_column, count(*) from t1";
+execute stmt;
+ERROR HY000: Can't reopen table: 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+prepare stmt from "select count(*), f1() as my_column from t1";
+execute stmt;
+ERROR HY000: Can't reopen table: 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+# Execute fails, no drop of temporary table
+prepare stmt from "select 1 as my_column from (select 1) as t2
+                   where exists (select f1() from t1)";
+execute stmt;
+my_column
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+my_column
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+# Execute drops temporary table
+prepare stmt from "select f1()";
+execute stmt;
+f1()
+1
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+ERROR 42S02: Unknown table 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+drop function f1;
+drop procedure p1;
+deallocate prepare stmt;
+# Execute fails, temporary table is not replaced by another
+create temporary table t1 as select 1 as a;
+create procedure p1()
+begin
+drop temporary table t1;
+create temporary table t1 as select 'abc' as a;
+end|
+create function f1() returns int
+begin
+call p1();
+return 1;
+end|
+prepare stmt from "select count(*), f1() as my_column from t1";
+execute stmt;
+ERROR HY000: Can't reopen table: 't1'
+call p_verify_reprepare_count(0);
+SUCCESS
+
+select * from t1;
+a
+1
+deallocate prepare stmt;
+prepare stmt from "call p1";
+execute stmt;
+drop procedure p1;
+create schema mysqltest;
+create procedure mysqltest.p1()
+begin
+drop schema mysqltest;
+create schema mysqltest;
+end|
+execute stmt;
+ERROR 42000: PROCEDURE test.p1 does not exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+execute stmt;
+ERROR 42000: PROCEDURE test.p1 does not exist
+call p_verify_reprepare_count(0);
+SUCCESS
+
+deallocate prepare stmt;
+drop schema mysqltest;
+drop temporary table t1;
+# Cleanup
+#
+drop temporary table if exists t1;
+drop table if exists t1, t2;
+drop procedure if exists p_verify_reprepare_count;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists t1;
+drop schema if exists mysqltest;

=== modified file 'mysql-test/r/query_cache_merge.result'
--- a/mysql-test/r/query_cache_merge.result	2003-08-22 01:07:40 +0000
+++ b/mysql-test/r/query_cache_merge.result	2008-04-08 16:01:20 +0000
@@ -18,3 +18,4 @@
 Qcache_queries_in_cache	0
 drop table
t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40,t41,t42,t43,t44,t45,t46,t47,t48,t49,t50,t51,t52,t53,t54,t55,t56,t57,t58,t59,t60,t61,t62,t63,t64,t65,t66,t67,t68,t69,t70,t71,t72,t73,t74,t75,t76,t77,t78,t79,t80,t81,t82,t83,t84,t85,t86,t87,t88,t89,t90,t91,t92,t93,t94,t95,t96,t97,t98,t99,t100,t101,t102,t103,t104,t105,t106,t107,t108,t109,t110,t111,t112,t113,t114,t115,t116,t117,t118,t119,t120,t121,t122,t123,t124,t125,t126,t127,t128,t129,t130,t131,t132,t133,t134,t135,t136,t137,t138,t139,t140,t141,t142,t143,t144,t145,t146,t147,t148,t149,t150,t151,t152,t153,t154,t155,t156,t157,t158,t159,t160,t161,t162,t163,t164,t165,t166,t167,t168,t169,t170,t171,t172,t173,t174,t175,t176,t177,t178,t179,t180,t181,t182,t183,t184,t185,t186,t187,t188,t189,t190,t191,t192,t193,t194,t195,t196,t197,t198,t199,t200,t201,t202,t203,t204,t205,t206,t207,t208,t209,t210,t211,t212,t213,t214,t215,t216,t217,!
 t218,t219,t220,t221,t222,t223,t224,t225,t226,t227,t228,t229,t230,t231,t232,t233,t234,t235,t236,t237,t238,t239,t240,t241,t242,t243,t244,t245,t246,t247,t248,t249,t250,t251,t252,t253,t254,t255,t256,t257,t00;
 SET @@global.query_cache_size=0;
+set @@global.table_definition_cache=@save_table_definition_cache;

=== modified file 'mysql-test/r/table_definition_cache_basic.result'
--- a/mysql-test/r/table_definition_cache_basic.result	2008-04-10 13:14:28 +0000
+++ b/mysql-test/r/table_definition_cache_basic.result	2008-05-20 17:07:11 +0000
@@ -1,27 +1,33 @@
 SET @start_value = @@global.table_definition_cache;
 SELECT @start_value;
 @start_value
-128
+256
 '#--------------------FN_DYNVARS_019_01------------------------#'
 SET @@global.table_definition_cache = 100;
+Warnings:
+Warning	1292	Truncated incorrect table_definition_cache value: '100'
 SET @@global.table_definition_cache = DEFAULT;
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-128
+256
 '#---------------------FN_DYNVARS_019_02-------------------------#'
 SET @@global.table_definition_cache = DEFAULT;
 SELECT @@global.table_definition_cache = 128;
 @@global.table_definition_cache = 128
-1
+0
 '#--------------------FN_DYNVARS_019_03------------------------#'
 SET @@global.table_definition_cache = 1;
+Warnings:
+Warning	1292	Truncated incorrect table_definition_cache value: '1'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-1
+256
 SET @@global.table_definition_cache = 2;
+Warnings:
+Warning	1292	Truncated incorrect table_definition_cache value: '2'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-2
+256
 SET @@global.table_definition_cache = 524287;
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
@@ -36,13 +42,13 @@
 Warning	1292	Truncated incorrect table_definition_cache value: '0'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-1
+256
 SET @@global.table_definition_cache = -1024;
 Warnings:
 Warning	1292	Truncated incorrect table_definition_cache value: '0'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-1
+256
 SET @@global.table_definition_cache = 524289;
 Warnings:
 Warning	1292	Truncated incorrect table_definition_cache value: '524289'
@@ -73,17 +79,21 @@
 1
 '#---------------------FN_DYNVARS_019_07----------------------#'
 SET @@global.table_definition_cache = TRUE;
+Warnings:
+Warning	1292	Truncated incorrect table_definition_cache value: '1'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-1
+256
 SET @@global.table_definition_cache = FALSE;
 Warnings:
 Warning	1292	Truncated incorrect table_definition_cache value: '0'
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-1
+256
 '#---------------------FN_DYNVARS_019_08----------------------#'
 SET @@global.table_definition_cache = 5;
+Warnings:
+Warning	1292	Truncated incorrect table_definition_cache value: '5'
 SELECT @@table_definition_cache = @@global.table_definition_cache;
 @@table_definition_cache = @@global.table_definition_cache
 1
@@ -99,4 +109,4 @@
 SET @@global.table_definition_cache = @start_value;
 SELECT @@global.table_definition_cache;
 @@global.table_definition_cache
-128
+256

=== modified file 'mysql-test/r/trigger.result'
--- a/mysql-test/r/trigger.result	2008-02-12 11:44:27 +0000
+++ b/mysql-test/r/trigger.result	2008-04-08 16:01:20 +0000
@@ -820,7 +820,6 @@
 drop trigger t1_bi;
 create trigger t1_bi after insert on t1 for each row insert into t3 values (new.id);
 execute stmt1;
-ERROR 42S02: Table 'test.t3' doesn't exist
 call p1();
 ERROR 42S02: Table 'test.t3' doesn't exist
 deallocate prepare stmt1;

=== modified file 'mysql-test/suite/rpl/r/rpl_heartbeat.result'
--- a/mysql-test/suite/rpl/r/rpl_heartbeat.result	2008-04-02 09:33:21 +0000
+++ b/mysql-test/suite/rpl/r/rpl_heartbeat.result	2008-05-21 19:44:56 +0000
@@ -11,13 +11,13 @@
 Value	5.000
 change master to master_host='127.0.0.1',master_port=MASTER_PORT, master_user='root',
master_heartbeat_period= 0.0009999;
 Warnings:
-Warning	1669	The requested value for the heartbeat period  is less than 1 msec.  The
period is reset to zero which means no heartbeats will be sending
+Warning	1670	The requested value for the heartbeat period  is less than 1 msec.  The
period is reset to zero which means no heartbeats will be sending
 show status like 'Slave_heartbeat_period';;
 Variable_name	Slave_heartbeat_period
 Value	0.000
 change master to master_host='127.0.0.1',master_port=MASTER_PORT, master_user='root',
master_heartbeat_period= 4294967;
 Warnings:
-Warning	1669	The requested value for the heartbeat period  exceeds the value of
`slave_net_timeout' sec.  A sensible value for the period should be less than the
timeout.
+Warning	1670	The requested value for the heartbeat period  exceeds the value of
`slave_net_timeout' sec.  A sensible value for the period should be less than the
timeout.
 show status like 'Slave_heartbeat_period';;
 Variable_name	Slave_heartbeat_period
 Value	4294967.000
@@ -29,7 +29,7 @@
 set @@global.slave_net_timeout= 5;
 change master to master_host='127.0.0.1',master_port=MASTER_PORT, master_user='root',
master_heartbeat_period= 5.001;
 Warnings:
-Warning	1669	The requested value for the heartbeat period  exceeds the value of
`slave_net_timeout' sec.  A sensible value for the period should be less than the
timeout.
+Warning	1670	The requested value for the heartbeat period  exceeds the value of
`slave_net_timeout' sec.  A sensible value for the period should be less than the
timeout.
 show status like 'Slave_heartbeat_period';;
 Variable_name	Slave_heartbeat_period
 Value	5.001
@@ -41,7 +41,7 @@
 Value	4.000
 set @@global.slave_net_timeout= 3 /* must be a warning */;
 Warnings:
-Warning	1669	The currect value for master_heartbeat_period exceeds the new value of
`slave_net_timeout' sec. A sensible value for the period should be less than the timeout.
+Warning	1670	The currect value for master_heartbeat_period exceeds the new value of
`slave_net_timeout' sec. A sensible value for the period should be less than the timeout.
 reset slave;
 drop table if exists t1;
 set @@global.slave_net_timeout= 10;

=== modified file 'mysql-test/suite/rpl/r/rpl_locktrans_innodb.result'
--- a/mysql-test/suite/rpl/r/rpl_locktrans_innodb.result	2008-04-02 09:33:21 +0000
+++ b/mysql-test/suite/rpl/r/rpl_locktrans_innodb.result	2008-05-21 19:44:56 +0000
@@ -100,12 +100,12 @@
 # Implicit lock method conversion due to mix in statement.
 LOCK TABLE t1 READ, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 # Lock t1 share (converted to read), t2 write.
 LOCK TABLE t1 IN SHARE MODE, t2 WRITE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't1'
 # Show t1 is read locked, t2 write locked.
 INSERT INTO t1 SELECT * FROM t2;
 ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
@@ -116,8 +116,8 @@
 # Lock t1 exclusive (converted to write), t2 share (converted to read).
 LOCK TABLE t1 IN EXCLUSIVE MODE, t2 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 # Show t1 is write locked, t2 read locked.
 INSERT INTO t1 SELECT * FROM t2;
 INSERT INTO t2 SELECT * FROM t1;
@@ -141,8 +141,8 @@
 ## Error is reported on first table only. Show both errors:
 SHOW WARNINGS;
 Level	Code	Message
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't1'
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't2'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't1'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't2'
 UNLOCK TABLES;
 SET @@SQL_MODE= @wl3561_save_sql_mode;
 #
@@ -162,7 +162,7 @@
 # Request a transactional lock, which is converted to non-transactional.
 LOCK TABLE t4 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Try a conflict with the existing non-transactional lock.
 INSERT INTO t4 VALUES(444);
 ERROR HY000: Table 't4' was locked with a READ lock and can't be updated
@@ -179,8 +179,8 @@
 # Request a share lock on the view, which is converted to read locks.
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Show that read locks on the base tables prohibit writing ...
 INSERT INTO t3 SELECT * FROM t4;
 ERROR HY000: Table 't3' was locked with a READ lock and can't be updated
@@ -196,7 +196,7 @@
 ## Report conversion on view due to existing non-transactional locks.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);
@@ -205,8 +205,8 @@
 ## Now report conversion on base table again.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);

=== modified file 'mysql-test/suite/rpl/r/rpl_locktrans_myisam.result'
--- a/mysql-test/suite/rpl/r/rpl_locktrans_myisam.result	2008-04-14 10:15:04 +0000
+++ b/mysql-test/suite/rpl/r/rpl_locktrans_myisam.result	2008-05-21 19:44:56 +0000
@@ -27,8 +27,8 @@
 # Valid syntax for transactional locks.
 LOCK TABLE t1 IN SHARE MODE, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 #
 # Valid syntax for aliases with and without 'AS'.
@@ -36,19 +36,19 @@
 UNLOCK TABLES;
 LOCK TABLE t1 AS a1 IN SHARE MODE, t2 a2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'a1'
-Warning	1613	Converted to non-transactional lock on 'a2'
+Warning	1614	Converted to non-transactional lock on 'a1'
+Warning	1614	Converted to non-transactional lock on 'a2'
 UNLOCK TABLES;
 #
 # Transactional locks taken on a view.
 CREATE VIEW v1 AS SELECT * FROM t1, t2 WHERE t1.c1 = t2.c2;
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 DROP VIEW v1;
 #
 # Locking INFORMATION_SCHEMA fails on missing privileges.
@@ -100,12 +100,12 @@
 # Implicit lock method conversion due to mix in statement.
 LOCK TABLE t1 READ, t2 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't2'
 UNLOCK TABLES;
 # Lock t1 share (converted to read), t2 write.
 LOCK TABLE t1 IN SHARE MODE, t2 WRITE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't1'
 # Show t1 is read locked, t2 write locked.
 INSERT INTO t1 SELECT * FROM t2;
 ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
@@ -116,8 +116,8 @@
 # Lock t1 exclusive (converted to write), t2 share (converted to read).
 LOCK TABLE t1 IN EXCLUSIVE MODE, t2 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't1'
-Warning	1613	Converted to non-transactional lock on 't2'
+Warning	1614	Converted to non-transactional lock on 't1'
+Warning	1614	Converted to non-transactional lock on 't2'
 # Show t1 is write locked, t2 read locked.
 INSERT INTO t1 SELECT * FROM t2;
 INSERT INTO t2 SELECT * FROM t1;
@@ -141,8 +141,8 @@
 ## Error is reported on first table only. Show both errors:
 SHOW WARNINGS;
 Level	Code	Message
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't1'
-Error	1614	Cannot convert to non-transactional lock in strict mode on 't2'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't1'
+Error	1615	Cannot convert to non-transactional lock in strict mode on 't2'
 UNLOCK TABLES;
 SET @@SQL_MODE= @wl3561_save_sql_mode;
 #
@@ -162,7 +162,7 @@
 # Request a transactional lock, which is converted to non-transactional.
 LOCK TABLE t4 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Try a conflict with the existing non-transactional lock.
 INSERT INTO t4 VALUES(444);
 ERROR HY000: Table 't4' was locked with a READ lock and can't be updated
@@ -179,8 +179,8 @@
 # Request a share lock on the view, which is converted to read locks.
 LOCK TABLE v1 IN SHARE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 # Show that read locks on the base tables prohibit writing ...
 INSERT INTO t3 SELECT * FROM t4;
 ERROR HY000: Table 't3' was locked with a READ lock and can't be updated
@@ -196,7 +196,7 @@
 ## Report conversion on view due to existing non-transactional locks.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 'v1'
+Warning	1614	Converted to non-transactional lock on 'v1'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);
@@ -205,8 +205,8 @@
 ## Now report conversion on base table again.
 LOCK TABLE v1 IN EXCLUSIVE MODE;
 Warnings:
-Warning	1613	Converted to non-transactional lock on 't3'
-Warning	1613	Converted to non-transactional lock on 't4'
+Warning	1614	Converted to non-transactional lock on 't3'
+Warning	1614	Converted to non-transactional lock on 't4'
 INSERT INTO t3 VALUES(333);
 INSERT INTO t4 VALUES(444);
 INSERT INTO t1 VALUES(111);

=== modified file 'mysql-test/t/disabled.def'
--- a/mysql-test/t/disabled.def	2008-05-21 12:38:58 +0000
+++ b/mysql-test/t/disabled.def	2008-05-23 21:39:20 +0000
@@ -19,7 +19,6 @@
 rpl_log_pos          : Bug#8693 Test 'rpl_log_pos' fails sometimes
 subselect_sj         : Bug#32994: subselect_sj fails sporadically (timeout)
 ctype_create         : Bug#32965 main.ctype_create fails
-ps_ddl               : Bug#12093 2007-12-14 pending WL#4165 / WL#4166
 user_limits          : Bug#23921 2007-12-16 random failure of user_limits.test
 backup                :BUG#34235 pending replacement of test facility with WL#4259
 backup_commit_blocker :BUG#34235 pending replacement of test facility with WL#4259
@@ -41,3 +40,9 @@
 log_output_basic      : Bug#34820 log_output can be set to illegal value
 query_cache_size_basic_32   : Bug#36747: Allocating a large query cache is not
deterministic
 query_cache_size_basic_64   : Bug#36747: Allocating a large query cache is not
deterministic
+wait_timeout_func           : Bug #36873 wait_timeout_func.test fails randomly
+slow_launch_time_func       : Bug #36874 main.slow_launch_time_func test fails randomly
+sort_buffer_size_basic_32   : Bug#36875 main.sort_buffer_size_basic_32 fails on some
systems
+key_buffer_size_basic_32    : Bug#36876 main.key_buffer_size_basic_32 fails on some
systems
+max_heap_table_size_basic_32 : Bug#36877 main.max_heap_table_size_basic_32 fails on some
systems
+tmp_table_size_basic_32     : Bug#36878 main.tmp_table_size_basic_32 fails on some
systems

=== modified file 'mysql-test/t/ps.test'
--- a/mysql-test/t/ps.test	2008-03-05 10:32:49 +0000
+++ b/mysql-test/t/ps.test	2008-05-21 10:17:29 +0000
@@ -2979,6 +2979,24 @@
 deallocate prepare stmt;
 drop table t1, t2;
 
+--echo #
+--echo # Bug#27430 Crash in subquery code when in PS and table DDL changed
+--echo # after PREPARE
+--echo #
+--echo # This part of the test doesn't work in embedded server, this is
+--echo # why it's here. For the main test see ps_ddl*.test
+--echo
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show events where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
 
 
 --echo End of 5.1 tests.

=== modified file 'mysql-test/t/ps_1general.test'
--- a/mysql-test/t/ps_1general.test	2007-08-26 08:36:11 +0000
+++ b/mysql-test/t/ps_1general.test	2008-05-21 10:17:29 +0000
@@ -181,7 +181,7 @@
   a int primary key,
   b char(30),
   c int,
-  d timestamp default current_timestamp
+  d timestamp default '2008-02-23 09:23:45'
 );
 insert into t5( a, b, c) values( 9, 'recreated table', 9);
 execute stmt2 ;
@@ -191,7 +191,7 @@
 create table t5
 (
   a int primary key,
-  d timestamp default current_timestamp,
+  d timestamp default '2008-02-23 09:23:45',
   b char(30),
   c int
 );
@@ -218,7 +218,6 @@
   f3 int
 );
 insert into t5( f1, f2, f3) values( 9, 'recreated table', 9);
---error 1054
 execute stmt2 ;
 drop table t5 ;
 

=== modified file 'mysql-test/t/ps_ddl.test'
--- a/mysql-test/t/ps_ddl.test	2008-04-07 09:35:42 +0000
+++ b/mysql-test/t/ps_ddl.test	2008-05-21 19:44:56 +0000
@@ -104,6 +104,7 @@
 --echo Part 4: TABLE -> NOTHING transitions
 --echo =====================================================================
 
+--echo # Test 4-a: select ... from <table>
 create table t1 (a int);
 
 prepare stmt from "select * from t1";
@@ -121,6 +122,25 @@
 call p_verify_reprepare_count(0);
 deallocate prepare stmt;
 
+--echo # Test 4-b: TABLE -> NOTHING by renaming the table
+create table t1 (a int);
+prepare stmt from "select * from t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+rename table t1 to t2;
+--error ER_NO_SUCH_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+--error ER_NO_SUCH_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+
+deallocate prepare stmt;
+drop table t2;
+
 --echo =====================================================================
 --echo Part 5: TABLE -> TABLE (DDL) transitions
 --echo =====================================================================
@@ -143,11 +163,11 @@
 drop table t1;
 deallocate prepare stmt;
 
+
 --echo =====================================================================
 --echo Part 6: TABLE -> TABLE (TRIGGER) transitions
 --echo =====================================================================
 
-
 --echo # Test 6-a: adding a relevant trigger
 
 create table t1 (a int);
@@ -181,7 +201,7 @@
 # Unrelated trigger: reprepare may or may not happen, implementation dependent
 create trigger t1_bd before delete on t1 for each row
   set @message= old.a;
-  
+
 set @val=5;
 execute stmt using @val;
 call p_verify_reprepare_count(1);
@@ -229,7 +249,7 @@
 call p_verify_reprepare_count(1);
 select @message;
 
---ehco Test 6-e: removing a relevant trigger
+--echo Test 6-e: removing a relevant trigger
 
 drop trigger t1_bi;
 
@@ -338,7 +358,7 @@
 --echo #
 --echo # Sic: the insert went into t3, even though the view now
 --echo # points at t2. This is because neither the merged view
---echo #  nor its prelocking list are affected by view DDL 
+--echo #  nor its prelocking list are affected by view DDL
 --echo # The binary log is of course wrong, since it is not
 --echo # using prepared statements
 --echo #
@@ -367,7 +387,7 @@
 --echo # Since the dependent table is tracked in the prelocked
 --echo # list of the prepared statement, invalidation happens
 --echo # and the statement is re-prepared. This is an unnecessary
---echo # side effect, since the statement that *is* dependent 
+--echo # side effect, since the statement that *is* dependent
 --echo # on t2 definition is inside the trigger, and it is currently
 --echo # not reprepared (see the previous test case).
 execute stmt using @var;
@@ -378,7 +398,7 @@
 
 --echo # Test 7-e: dependent TABLE TRIGGER has changed
 create table t1 (a int);
-create trigger t1_ai after insert on t1 for each row 
+create trigger t1_ai after insert on t1 for each row
   insert into t2 (a) values (new.a);
 create table t2 (a int unique);
 create trigger t2_ai after insert on t2 for each row
@@ -414,6 +434,7 @@
 --echo Part 8: TABLE -> TEMPORARY TABLE transitions
 --echo =====================================================================
 
+--echo # Test 8-a: base table used recreated as temporary table
 create table t1 (a int);
 
 prepare stmt from "select * from t1";
@@ -430,6 +451,25 @@
 drop table t1;
 deallocate prepare stmt;
 
+--echo # Test 8-b: temporary table has precedence over base table with same name
+create table t1 (a int);
+prepare stmt from 'select count(*) from t1';
+execute stmt;
+call p_verify_reprepare_count(0);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+create temporary table t1 AS SELECT 1;
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+deallocate prepare stmt;
+drop temporary table t1;
+drop table t1;
+
+
 --echo =====================================================================
 --echo Part 9: TABLE -> VIEW transitions
 --echo =====================================================================
@@ -471,6 +511,7 @@
 --echo Part 11: TEMPORARY TABLE -> TABLE transitions
 --echo =====================================================================
 
+--echo # Test 11-a: temporary table replaced by base table
 create table t1 (a int);
 insert into t1 (a) value (1);
 create temporary table t1 (a int);
@@ -488,6 +529,27 @@
 drop table t1;
 deallocate prepare stmt;
 
+
+--echo # Test 11-b: temporary table has precedence over base table with same name
+--echo #            temporary table disappears
+create table t1 (a int);
+create temporary table t1 as select 1 as a;
+prepare stmt from "select count(*) from t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+drop temporary table t1;
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+deallocate prepare stmt;
+drop table t1;
+
+
 --echo =====================================================================
 --echo Part 12: TEMPORARY TABLE -> TEMPORARY TABLE (DDL) transitions
 --echo =====================================================================
@@ -614,6 +676,8 @@
 --echo # Currently a different result from conventional statements.
 --echo # A view is inlined once at prepare, later on view DDL
 --echo # does not affect prepared statement and it is not re-prepared.
+--echo # This is reported in Bug#36002 Prepared statements: if a view
+--echo # used in a statement is replaced, bad data
 execute stmt;
 call p_verify_reprepare_count(0);
 flush table t2;
@@ -672,7 +736,7 @@
 create procedure p1(out x int) select max(a) from t2 into x;
 --echo # XXX: bug. The prelocked list is not invalidated
 --echo # and we keep opening table t1, whereas the procedure
---ehco # is now referring to table t2
+--echo # is now referring to table t2
 --error ER_VIEW_INVALID
 execute stmt;
 call p_verify_reprepare_count(0);
@@ -700,7 +764,7 @@
 call p_verify_reprepare_count(1);
 execute stmt;
 --echo # Test 18-d: dependent TABLE has changed
-drop view v2; 
+drop view v2;
 create table v2 as select * from t1;
 execute stmt;
 call p_verify_reprepare_count(1);
@@ -852,7 +916,7 @@
 drop table t1;
 create table t1 (a1 int, a2 int);
 
---echo # t1 has changed, and it's does not lead to reprepare 
+--echo # t1 has changed, and it's does not lead to reprepare
 execute stmt;
 
 alter table t1 drop column b;
@@ -879,7 +943,7 @@
 create table t1 (a1 int, a2 int);
 insert into t1 values (1, 10), (2, 20), (3, 30);
 
---echo # t1 has changed, and it's does not lead to reprepare 
+--echo # t1 has changed, and it's does not lead to reprepare
 execute stmt;
 
 alter table t1 add column b varchar(50) default NULL;
@@ -1188,16 +1252,16 @@
   (1234, 3),
   (1234, 4);
 
-prepare stmt from 
+prepare stmt from
  "select oref, a, a in (select a from t_27430_1 where oref=t_27430_2.oref) Z from
t_27430_2";
 
-execute stmt; 
+execute stmt;
 call p_verify_reprepare_count(0);
 
 drop table t_27430_1, t_27430_2;
 
 create table t_27430_1 (a int, oref int, key(a));
-insert into t_27430_1 values 
+insert into t_27430_1 values
   (1, 1),
   (1, NULL),
   (2, 3),
@@ -1235,7 +1299,7 @@
 create table v_27690_1 as select * from t_27690_1;
 create table v_27690_2 as select * from t_27690_1;
 
-prepare stmt from "select * from v_27690_1, v_27690_2"; 
+prepare stmt from "select * from v_27690_1, v_27690_2";
 
 execute stmt;
 execute stmt;
@@ -1325,17 +1389,17 @@
 connection default;
 
 --echo # XXX: bug
---error ER_SP_DOES_NOT_EXIST 
+--error ER_SP_DOES_NOT_EXIST
 execute stmt_sf;
 --echo # XXX: bug
---error ER_SP_DOES_NOT_EXIST 
+--error ER_SP_DOES_NOT_EXIST
 execute stmt_sp;
 
 --echo # XXX: bug
---error ER_SP_DOES_NOT_EXIST 
+--error ER_SP_DOES_NOT_EXIST
 execute stmt_sf;
 --echo # XXX: bug
---error ER_SP_DOES_NOT_EXIST 
+--error ER_SP_DOES_NOT_EXIST
 execute stmt_sp;
 call p_verify_reprepare_count(0);
 
@@ -1347,8 +1411,647 @@
 deallocate prepare stmt_sf;
 deallocate prepare stmt_sp;
 
+
+--echo =====================================================================
+--echo Ensure that metadata validation is performed for every type of
+--echo SQL statement where it is needed.
+--echo =====================================================================
+
+--echo #
+--echo # SQLCOM_SELECT
+--echo #
+
+# --disable_warnings
+# drop table if exists t1;
+# --enable_warnings
+# create table t1 (a int);
+# prepare stmt from "select 1 as res from dual where (1) in (select * from t1)";
+# drop table t1;
+# create table t1 (x int);
+# execute stmt;
+# drop table t1;
+# deallocate prepare stmt;
+# call p_verify_reprepare_count(1);
+
+--echo #
+--echo # SQLCOM_CREATE_TABLE
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+drop table if exists t2;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from 'create table t2 as select * from t1';
+execute stmt;
+drop table t2;
+execute stmt;
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+# Base table with name of table to be created exists
+--error ER_TABLE_EXISTS_ERROR
+execute stmt;
+call p_verify_reprepare_count(1);
+--error ER_TABLE_EXISTS_ERROR
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+# Temporary table with name of table to be created exists
+create temporary table t2 (a int);
+--error ER_TABLE_EXISTS_ERROR
+execute stmt;
+call p_verify_reprepare_count(1);
+--error ER_TABLE_EXISTS_ERROR
+execute stmt;
+call p_verify_reprepare_count(0);
+drop temporary table t2;
+execute stmt;
+call p_verify_reprepare_count(1);
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+# View with name of table to be created exists
+# Attention:
+#    We cannot print the error message because it contains a random filename.
+#    Example: 1050: Table '<some_path>/var/tmp/#sql_6979_0' already exists
+#    Therefore we mangle it via
+#    "--error ER_TABLE_EXISTS_ERROR,9999" (9999 is currently not used)
+#    to "Got one of the listed errors".
+create view t2 as select 1;
+--error ER_TABLE_EXISTS_ERROR,9999
+execute stmt;
+call p_verify_reprepare_count(1);
+--error ER_TABLE_EXISTS_ERROR,9999
+execute stmt;
+call p_verify_reprepare_count(0);
+drop view t2;
+drop table t1;
+# Table to be used recreated (drop,create) with different layout
+create table t1 (x varchar(20));
+execute stmt;
+call p_verify_reprepare_count(1);
+select * from t2;
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+# Table to be used has a modified (alter table) layout
+alter table t1 add column y decimal(10,3);
+execute stmt;
+call p_verify_reprepare_count(1);
+select * from t2;
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t1;
+deallocate prepare stmt;
+--echo # XXX: no validation of the first table in case of
+--echo # CREATE TEMPORARY TABLE. This is a shortcoming of the current code,
+--echo # but since validation is not strictly necessary, nothing is done
+--echo # about it.
+--echo # Will be fixed as part of work on Bug#21431 "Incomplete support of
+--echo # temporary tables"
+create table t1 (a int);
+insert into t1 (a) values (1);
+prepare stmt from "create temporary table if not exists t2 as select * from t1";
+execute stmt;
+drop table t2;
+execute stmt;
+execute stmt;
+select * from t2;
+execute stmt;
+select * from t2;
+drop table t2;
+create temporary table t2 (a varchar(10));
+execute stmt;
+select * from t2;
+call p_verify_reprepare_count(0);
+drop table t1;
+create table t1 (x int);
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t1;
+drop temporary table t2;
+drop table t2;
+deallocate prepare stmt;
+
+create table t1 (a int);
+prepare stmt from "create table t2 like t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+# Table to be used does not exist
+drop table t1;
+--error ER_NO_SUCH_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+--error ER_NO_SUCH_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+# Table to be used recreated (drop,create) with different layout
+create table t1 (x char(17));
+execute stmt;
+call p_verify_reprepare_count(1);
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t2;
+# Table to be used has a modified (alter table) layout
+alter table t1 add column y time;
+execute stmt;
+call p_verify_reprepare_count(1);
+select * from t2;
+drop table t2;
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t1;
+drop table t2;
+deallocate prepare stmt;
+
+
+--echo #
+--echo # SQLCOM_UPDATE
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "update t2 set a=a+1 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_INSERT
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "insert into t2 set a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_INSERT_SELECT
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "insert into t2 select * from t1";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_REPLACE
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "replace t2 set a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_REPLACE_SELECT
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "replace t2 select * from t1";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_DELETE
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+prepare stmt from "delete from t2 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_DELETE_MULTI
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+prepare stmt from "delete t2, t3 from t2, t3 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2, t3;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_UPDATE_MULTI
+--echo #
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+prepare stmt from "update t2, t3 set t3.a=t2.a, t2.a=null where (1) in (select * from
t1)";
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1, t2, t3;
+deallocate prepare stmt;
+--echo # Intermediate results: 8 SQLCOMs tested, 8 automatic reprepares
+call p_verify_reprepare_count(8);
+
+--echo #
+--echo # SQLCOM_LOAD
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a varchar(20));
+--error ER_UNSUPPORTED_PS
+prepare stmt from "load data infile '../std_data_ln/words.dat' into table t1";
+drop table t1;
+
+--echo #
+--echo # SQLCOM_SHOW_DATABASES
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show databases where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_TABLES
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show tables where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_FIELDS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show fields from t1 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_KEYS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show keys from t1 where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_VARIABLES
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show variables where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_STATUS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show status where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_LOGS,
+--echo # SQLCOM_SHOW_ENGINE_MUTEX, SQLCOM_SHOW_PROCESSLIST
+--echo #
+
+--echo # Currently can not have a where clause, need to be covered
+--echo # with tests
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+--error ER_PARSE_ERROR
+prepare stmt from "show engine all status where (1) in (select * from t1)";
+--error ER_PARSE_ERROR
+prepare stmt from "show engine all logs where (1) in (select * from t1)";
+--error ER_PARSE_ERROR
+prepare stmt from "show engine all mutex where (1) in (select * from t1)";
+--error ER_PARSE_ERROR
+prepare stmt from "show processlist where (1) in (select * from t1)";
+drop table t1;
+
+--echo #
+--echo # SQLCOM_SHOW_CHARSETS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show charset where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_COLLATIONS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show collation where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_TABLE_STATUS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show table status where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_TRIGGERS
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show triggers where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_OPEN_TABLES
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show open tables where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_STATUS_PROC
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show procedure status where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_STATUS_FUNC
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "show function status where (1) in (select * from t1)";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_SHOW_EVENTS
+--echo #
+--echo #
+--echo # Please see this test in ps.test, it requires not_embedded.inc
+--echo #
+
+--echo #
+--echo # SQLCOM_SET_OPTION
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "set @a=((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_DO
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "do ((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_CALL
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+drop procedure if exists p1;
+--enable_warnings
+create procedure p1(a int) begin end;
+create table t1 (a int);
+prepare stmt from "call p1((1) in (select * from t1))";
+execute stmt;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop table t1;
+drop procedure p1;
+deallocate prepare stmt;
+
+--echo #
+--echo # SQLCOM_CREATE_VIEW
+--echo #
+
+--disable_warnings
+drop table if exists t1;
+drop view if exists v1;
+--enable_warnings
+create table t1 (a int);
+prepare stmt from "create view v1 as select * from t1";
+execute stmt;
+drop view v1;
+drop table t1;
+create table t1 (x int);
+execute stmt;
+drop view v1;
+drop table t1;
+deallocate prepare stmt;
+--echo # Intermediate result: number of reprepares matches the number
+--echo # of tests
+call p_verify_reprepare_count(17);
+
+--echo #
+--echo # SQLCOM_ALTER_VIEW
+--echo #
+
+--disable_warnings
+drop view if exists v1;
+--enable_warnings
+create view v1 as select 1;
+--error ER_UNSUPPORTED_PS
+prepare stmt from "alter view v1 as select 2";
+drop view v1;
+
 --echo # Cleanup
---echo # 
+--echo #
 --disable_warnings
 drop temporary table if exists t1, t2, t3;
 drop table if exists t1, t2, t3, v1, v2;

=== added file 'mysql-test/t/ps_ddl1.test'
--- a/mysql-test/t/ps_ddl1.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/ps_ddl1.test	2008-04-18 19:18:53 +0000
@@ -0,0 +1,398 @@
+#
+# Testing the behavior of 'PREPARE', 'DDL', 'EXECUTE' scenarios
+#
+# There are several subtests which are probably "superfluous" because a DDL
+# statement before the EXECUTE <prepared stmt handle> contained a keyword
+# or action (Example: Alter) which causes that all prepared statements using
+# the modified object are reprepared before execution.
+# Please do not delete these subtests if they disturb. Just disable them by
+# if (0)
+# {
+#    <tests to disable>
+# }.
+# There might be future optimisations of the server which decrease the amount
+# of unneeded reprepares or skip unneeded prepare steps and than these subtests
+# might become valuable.
+#    Example:
+#    Every preceding ALTER TABLE seems to cause a reprepare.
+#    But if the ALTER only changed the table comment ...
+#
+# Created: 2008-04-18 mleich
+#
+
+--disable_warnings
+drop temporary table if exists t1;
+drop table if exists t1, t2;
+drop procedure if exists p_verify_reprepare_count;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists t1;
+drop schema if exists mysqltest;
+--enable_warnings
+
+delimiter |;
+create procedure p_verify_reprepare_count(expected int)
+begin
+  declare old_reprepare_count int default @reprepare_count;
+
+  select variable_value from
+  information_schema.session_status where
+  variable_name='com_stmt_reprepare'
+  into @reprepare_count;
+
+  if old_reprepare_count + expected <> @reprepare_count then
+    select concat("Expected: ", expected,
+                   ", actual: ", @reprepare_count - old_reprepare_count)
+    as "ERROR";
+  else
+    select '' as "SUCCESS";
+  end if;
+end|
+delimiter ;|
+set @reprepare_count= 0;
+flush status;
+
+--disable_warnings
+drop table if exists t1;
+--disable_warnings
+
+--echo # Column added or dropped is not within the list of selected columns
+--echo # or table comment has changed.
+--echo # A reprepare is probably not needed.
+create table t1 (a int, b int);
+prepare stmt from "select a from t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+alter table t1 add column c int;
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+alter table t1 drop column b;
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+alter table t1 comment "My best table";
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+drop table t1;
+deallocate prepare stmt;
+
+--echo # Selects using the table at various positions, inser,update ...
+--echo # + the table disappears
+create table t1 (a int);
+# Attention:
+#   "truncate" must have the first position (= executed as last prepared
+#   statement), because it recreates the table which has leads to reprepare
+#   (is this really needed) of all statements.
+prepare stmt1 from "truncate t1";
+prepare stmt2 from "select 1 as my_column from t1";
+prepare stmt3 from "select 1 as my_column from (select * from t1) as t2";
+prepare stmt4 from
+"select 1 as my_column from (select 1) as t2 where exists (select 1 from t1)";
+prepare stmt5 from "select * from (select 1 as b) as t2, t1";
+prepare stmt6 from "select * from t1 union all select 1.5";
+prepare stmt7 from "select 1 as my_column union all select 1 from t1";
+prepare stmt8 from "insert into t1 values(1),(2)";
+prepare stmt9 from "update t1 set a = 3 where a = 2";
+prepare stmt10 from "delete from t1 where a = 1";
+let ps_stmt_count= 10;
+--echo # Attention: Result logging is disabled.
+# Checks of correct results of statements are not the goal of this test.
+let $num= $ps_stmt_count;
+while ($num)
+{
+   --disable_result_log
+   eval execute stmt$num;
+   --enable_result_log
+   dec $num;
+}
+# There was no reprepare needed, because none of the objects has changed.
+call p_verify_reprepare_count(0);
+drop table t1;
+let $num= $ps_stmt_count;
+while ($num)
+{
+   --error ER_NO_SUCH_TABLE
+   eval execute stmt$num;
+   dec $num;
+}
+# There was no reprepare needed, because the statement is no more applicable.
+call p_verify_reprepare_count(0);
+let $num= $ps_stmt_count;
+while ($num)
+{
+   eval deallocate prepare stmt$num;
+   dec $num;
+}
+
+--echo # Selects using the table at various positions, inser,update ...
+--echo # + layout change (drop column) which must cause a reprepare
+create table t1 (a int, b int);
+insert into t1 values(1,1),(2,2),(3,3);
+create table t2 like t1;
+insert into t1 values(2,2);
+prepare stmt1 from "select a,b from t1";
+prepare stmt2 from "select a,b from (select * from t1) as t1";
+prepare stmt3 from "select * from t1 where a = 2 and b = 2";
+prepare stmt4 from "select * from t2 where (a,b) in (select * from t1)";
+prepare stmt5 from "select * from t1 union select * from t2";
+prepare stmt6 from "select * from t1 union all select * from t2";
+prepare stmt7 from "insert into t1 set a = 4, b = 4";
+prepare stmt8 from "insert into t1 select * from t2";
+let ps_stmt_count= 8;
+--echo # Attention: Result logging is disabled.
+# Checks of correct results of statements are not the goal of this test.
+let $num= $ps_stmt_count;
+while ($num)
+{
+   --disable_result_log
+   eval execute stmt$num;
+   --enable_result_log
+   dec $num;
+}
+call p_verify_reprepare_count(0);
+alter table t1 drop column b;
+--disable_abort_on_error
+let $num= $ps_stmt_count;
+while ($num)
+{
+   eval execute stmt$num;
+   # A reprepare is needed, because layout change of t1 affects statement.
+   call p_verify_reprepare_count(1);
+   dec $num;
+}
+let $num= $ps_stmt_count;
+while ($num)
+{
+   eval execute stmt$num;
+   call p_verify_reprepare_count(1);
+   dec $num;
+}
+--echo # Why does the INSERT ... SELECT does not get a reprepare or is
+--echo # only the counter not incremented?
+eval execute stmt8;
+call p_verify_reprepare_count(1);
+--enable_abort_on_error
+alter table t2 add column c int;
+--error ER_WRONG_VALUE_COUNT_ON_ROW
+eval execute stmt8;
+call p_verify_reprepare_count(1);
+let $num= $ps_stmt_count;
+while ($num)
+{
+   eval deallocate prepare stmt$num;
+   dec $num;
+}
+drop table t1;
+drop table t2;
+
+
+--echo # select AVG(<col>) + optimizer uses index meets loss of the index
+create table t1 (a int, b int, primary key(b),unique index t1_unq_idx(a));
+# We need an index which is not converted to PRIMARY KEY (becomes in
+# case of InnoDB the key used for table clustering).
+insert into t1 set a = 0, b = 0;
+insert into t1 select a + 1, b + 1 from t1;
+insert into t1 select a + 2, b + 2 from t1;
+insert into t1 select a + 4, b + 4 from t1;
+insert into t1 select a + 8, b + 8 from t1;
+# "using index" optimizer strategy is intended
+let $possible_keys=
+    query_get_value(explain select avg(a) from t1, possible_keys, 1);
+let $extra=
+    query_get_value(explain select avg(a) from t1, Extra, 1);
+--echo # Optimizer strategy: Possible keys = $possible_keys , Extra = $extra
+prepare stmt from "select avg(a) from t1";
+execute stmt;
+call p_verify_reprepare_count(0);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+alter table t1 drop index t1_unq_idx;
+let $possible_keys=
+    query_get_value(explain select avg(a) from t1, possible_keys, 1);
+let $extra=
+    query_get_value(explain select avg(a) from t1, Extra, 1);
+--echo # Optimizer strategy: Possible keys = $possible_keys , Extra = $extra
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+
+--echo # select AVG(<col>) + optimizer uses table scan meets a new index
+alter table t1 add unique index t1_unq_idx(a);
+let $possible_keys=
+    query_get_value(explain select avg(a) from t1, possible_keys, 1);
+let $extra=
+    query_get_value(explain select avg(a) from t1, Extra, 1);
+--echo # Optimizer strategy: Possible keys = $possible_keys , Extra = $extra
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+
+deallocate prepare stmt;
+drop table t1;
+
+
+--echo # table replaced by not updatable view - Insert
+create table t1 (a int);
+prepare stmt from "insert into t1 values(1)";
+execute stmt;
+call p_verify_reprepare_count(0);
+
+drop table t1;
+create view t1 as select 1;
+--error ER_NON_INSERTABLE_TABLE
+execute stmt;
+call p_verify_reprepare_count(1);
+
+drop view t1;
+create table t2 (a int);
+create view t1 as select * from t2 with check option;
+execute stmt;
+call p_verify_reprepare_count(1);
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+deallocate prepare stmt;
+drop view t1;
+drop table t2;
+
+
+--echo =====================================================================
+--echo Some freestyle tests
+--echo =====================================================================
+
+create temporary table t1 as select 1 as a;
+delimiter |;
+create procedure p1()
+begin
+  drop temporary table t1;
+end|
+create function f1() returns int
+begin
+  call p1();
+  return 1;
+end|
+delimiter ;|
+
+prepare stmt from "select f1() as my_column, a from t1";
+--error ER_CANT_REOPEN_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+prepare stmt from "select a, f1() as my_column from t1";
+--error ER_CANT_REOPEN_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+prepare stmt from "select f1() as my_column, count(*) from t1";
+--error ER_CANT_REOPEN_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+prepare stmt from "select count(*), f1() as my_column from t1";
+--error ER_CANT_REOPEN_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+
+--echo # Execute fails, no drop of temporary table
+prepare stmt from "select 1 as my_column from (select 1) as t2
+                   where exists (select f1() from t1)";
+execute stmt;
+call p_verify_reprepare_count(0);
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+
+--echo # Execute drops temporary table
+prepare stmt from "select f1()";
+execute stmt;
+call p_verify_reprepare_count(0);
+--error ER_BAD_TABLE_ERROR
+execute stmt;
+call p_verify_reprepare_count(0);
+
+drop function f1;
+drop procedure p1;
+deallocate prepare stmt;
+
+--echo # Execute fails, temporary table is not replaced by another
+create temporary table t1 as select 1 as a;
+delimiter |;
+create procedure p1()
+begin
+  drop temporary table t1;
+  create temporary table t1 as select 'abc' as a;
+end|
+create function f1() returns int
+begin
+  call p1();
+  return 1;
+end|
+delimiter ;|
+prepare stmt from "select count(*), f1() as my_column from t1";
+--error ER_CANT_REOPEN_TABLE
+execute stmt;
+call p_verify_reprepare_count(0);
+select * from t1;
+deallocate prepare stmt;
+
+prepare stmt from "call p1";
+execute stmt;
+drop procedure p1;
+create schema mysqltest;
+delimiter |;
+create procedure mysqltest.p1()
+begin
+   drop schema mysqltest;
+   create schema mysqltest;
+end|
+delimiter ;|
+--error ER_SP_DOES_NOT_EXIST
+execute stmt;
+call p_verify_reprepare_count(0);
+--error ER_SP_DOES_NOT_EXIST
+execute stmt;
+call p_verify_reprepare_count(0);
+deallocate prepare stmt;
+drop schema mysqltest;
+drop temporary table t1;
+
+
+# Bug#36089 drop temp table in SP called by function, crash
+# Note: A non prepared "select 1 from t1 having count(*) = f1();" is sufficient. 
+if (0)
+{
+create temporary table t1 as select 1 as a;
+prepare stmt from "select 1 from t1 having count(*) = f1()";
+execute stmt;
+call p_verify_reprepare_count(0);
+deallocate prepare stmt;
+drop temporary table t1;
+}
+
+
+--echo # Cleanup
+--echo #
+--disable_warnings
+drop temporary table if exists t1;
+drop table if exists t1, t2;
+drop procedure if exists p_verify_reprepare_count;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists t1;
+drop schema if exists mysqltest;
+--enable_warnings

=== modified file 'mysql-test/t/query_cache_merge.test'
--- a/mysql-test/t/query_cache_merge.test	2005-07-28 00:22:47 +0000
+++ b/mysql-test/t/query_cache_merge.test	2008-04-08 16:01:20 +0000
@@ -25,6 +25,15 @@
 }
 --enable_warnings
 
+#
+# In order for the test to pass in --ps-protocol, we must
+# set table_definition_cache size to at least 258 elements.
+# Otherwise table versions are bound to change between
+# prepare and execute, and we will get a constant validation
+# error. See WL#4165 for details.
+#
+set @save_table_definition_cache= @@global.table_definition_cache;
+set @@global.table_definition_cache=512;
 create table t00 (a int) engine=MERGE
UNION=(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40,t41,t42,t43,t44,t45,t46,t47,t48,t49,t50,t51,t52,t53,t54,t55,t56,t57,t58,t59,t60,t61,t62,t63,t64,t65,t66,t67,t68,t69,t70,t71,t72,t73,t74,t75,t76,t77,t78,t79,t80,t81,t82,t83,t84,t85,t86,t87,t88,t89,t90,t91,t92,t93,t94,t95,t96,t97,t98,t99,t100,t101,t102,t103,t104,t105,t106,t107,t108,t109,t110,t111,t112,t113,t114,t115,t116,t117,t118,t119,t120,t121,t122,t123,t124,t125,t126,t127,t128,t129,t130,t131,t132,t133,t134,t135,t136,t137,t138,t139,t140,t141,t142,t143,t144,t145,t146,t147,t148,t149,t150,t151,t152,t153,t154,t155,t156,t157,t158,t159,t160,t161,t162,t163,t164,t165,t166,t167,t168,t169,t170,t171,t172,t173,t174,t175,t176,t177,t178,t179,t180,t181,t182,t183,t184,t185,t186,t187,t188,t189,t190,t191,t192,t193,t194,t195,t196,t197,t198,t199,t200,t201,t202,t203,t204,t205,t206,t207,t208,t209,t210,t!
 211,t212,t213,t214,t215,t216,t217,t218,t219,t220,t221,t222,t223,t224,t225,t226,t227,t228,t229,t230,t231,t232,t233,t234,t235,t236,t237,t238,t239,t240,t241,t242,t243,t244,t245,t246,t247,t248,t249,t250,t251,t252,t253,t254,t255,t256,t257)
INSERT_METHOD=FIRST;
 enable_query_log;
 select count(*) from t00;
@@ -36,5 +45,6 @@
 drop table
t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40,t41,t42,t43,t44,t45,t46,t47,t48,t49,t50,t51,t52,t53,t54,t55,t56,t57,t58,t59,t60,t61,t62,t63,t64,t65,t66,t67,t68,t69,t70,t71,t72,t73,t74,t75,t76,t77,t78,t79,t80,t81,t82,t83,t84,t85,t86,t87,t88,t89,t90,t91,t92,t93,t94,t95,t96,t97,t98,t99,t100,t101,t102,t103,t104,t105,t106,t107,t108,t109,t110,t111,t112,t113,t114,t115,t116,t117,t118,t119,t120,t121,t122,t123,t124,t125,t126,t127,t128,t129,t130,t131,t132,t133,t134,t135,t136,t137,t138,t139,t140,t141,t142,t143,t144,t145,t146,t147,t148,t149,t150,t151,t152,t153,t154,t155,t156,t157,t158,t159,t160,t161,t162,t163,t164,t165,t166,t167,t168,t169,t170,t171,t172,t173,t174,t175,t176,t177,t178,t179,t180,t181,t182,t183,t184,t185,t186,t187,t188,t189,t190,t191,t192,t193,t194,t195,t196,t197,t198,t199,t200,t201,t202,t203,t204,t205,t206,t207,t208,t209,t210,t211,t212,t213,t214,t215,t216,t217,!
 t218,t219,t220,t221,t222,t223,t224,t225,t226,t227,t228,t229,t230,t231,t232,t233,t234,t235,t236,t237,t238,t239,t240,t241,t242,t243,t244,t245,t246,t247,t248,t249,t250,t251,t252,t253,t254,t255,t256,t257,t00;
 
 SET @@global.query_cache_size=0;
+set @@global.table_definition_cache=@save_table_definition_cache;
 
 # End of 4.1 tests

=== modified file 'mysql-test/t/trigger.test'
--- a/mysql-test/t/trigger.test	2008-02-12 11:44:27 +0000
+++ b/mysql-test/t/trigger.test	2008-04-08 16:01:20 +0000
@@ -997,11 +997,10 @@
 # Altering trigger forcing it use different set of tables
 drop trigger t1_bi;
 create trigger t1_bi after insert on t1 for each row insert into t3 values (new.id);
-# Until we implement proper mechanism for invalidation of PS/SP when table
-# or SP's are changed these two statements will fail with 'Table ... was
-# not locked' error (this mechanism should be based on the new TDC).
---error ER_NO_SUCH_TABLE 
 execute stmt1;
+# Until we implement proper mechanism for invalidation of SP statements
+# invoked whenever a table used in SP changes, this statement will fail with
+# 'Table ...  does not exist' error.
 --error ER_NO_SUCH_TABLE 
 call p1();
 deallocate prepare stmt1;

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2008-05-08 20:43:28 +0000
+++ b/sql/item.cc	2008-05-21 10:17:29 +0000
@@ -3175,6 +3175,49 @@
 }
 
 
+/**
+  Preserve the original parameter types and values
+  when re-preparing a prepared statement.
+
+  @details Copy parameter type information and conversion
+  function pointers from a parameter of the old statement
+  to the corresponding parameter of the new one.
+
+  Move parameter values from the old parameters to the new
+  one. We simply "exchange" the values, which allows
+  to save on allocation and character set conversion in
+  case a parameter is a string or a blob/clob.
+
+  The old parameter gets the value of this one, which
+  ensures that all memory of this parameter is freed
+  correctly.
+
+  @param[in]  src   parameter item of the original
+                    prepared statement
+*/
+
+void
+Item_param::set_param_type_and_swap_value(Item_param *src)
+{
+  unsigned_flag= src->unsigned_flag;
+  param_type= src->param_type;
+  set_param_func= src->set_param_func;
+  item_type= src->item_type;
+  item_result_type= src->item_result_type;
+
+  collation.set(src->collation);
+  maybe_null= src->maybe_null;
+  null_value= src->null_value;
+  max_length= src->max_length;
+  decimals= src->decimals;
+  state= src->state;
+  value= src->value;
+
+  decimal_value.swap(src->decimal_value);
+  str_value.swap(src->str_value);
+  str_value_ptr.swap(src->str_value_ptr);
+}
+
 /****************************************************************************
   Item_copy_string
 ****************************************************************************/

=== modified file 'sql/item.h'
--- a/sql/item.h	2008-03-14 22:21:29 +0000
+++ b/sql/item.h	2008-05-21 10:17:29 +0000
@@ -1739,6 +1739,7 @@
   bool eq(const Item *item, bool binary_cmp) const;
   /** Item is a argument to a limit clause. */
   bool limit_clause_param;
+  void set_param_type_and_swap_value(Item_param *from);
 };
 
 

=== modified file 'sql/my_decimal.h'
--- a/sql/my_decimal.h	2008-04-28 18:12:52 +0000
+++ b/sql/my_decimal.h	2008-05-20 07:38:17 +0000
@@ -114,6 +114,14 @@
   bool sign() const { return decimal_t::sign; }
   void sign(bool s) { decimal_t::sign= s; }
   uint precision() const { return intg + frac; }
+
+  /** Swap two my_decimal values */
+  void swap(my_decimal &rhs)
+  {
+    swap_variables(my_decimal, *this, rhs);
+    /* Swap the buffer pointers back */
+    swap_variables(decimal_digit_t *, buf, rhs.buf);
+  }
 };
 
 

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-05-08 16:01:15 +0000
+++ b/sql/mysql_priv.h	2008-05-21 10:17:29 +0000
@@ -299,6 +299,21 @@
 #define USER_VARS_HASH_SIZE     16
 #define TABLE_OPEN_CACHE_MIN    64
 #define TABLE_OPEN_CACHE_DEFAULT 64
+#define TABLE_DEF_CACHE_DEFAULT 256
+/**
+  We must have room for at least 256 table definitions in the table
+  cache, since otherwise there is no chance prepared
+  statements that use these many tables can work.
+  Prepared statements use table definition cache ids (table_map_id)
+  as table version identifiers. If the table definition
+  cache size is less than the number of tables used in a statement,
+  the contents of the table definition cache is guaranteed to rotate
+  between a prepare and execute. This leads to stable validation
+  errors. In future we shall use more stable version identifiers,
+  for now the only solution is to ensure that the table definition
+  cache can contain at least all tables of a given statement.
+*/
+#define TABLE_DEF_CACHE_MIN     256
 
 /* 
  Value of 9236 discovered through binary search 2006-09-26 on Ubuntu Dapper
@@ -720,6 +735,31 @@
                               const char *calling_file, 
                               const unsigned int calling_line);
 
+/**
+  Enumerate possible types of a table from re-execution
+  standpoint.
+  TABLE_LIST class has a member of this type.
+  At prepared statement prepare, this member is assigned a value
+  as of the current state of the database. Before (re-)execution
+  of a prepared statement, we check that the value recorded at
+  prepare matches the type of the object we obtained from the
+  table definition cache.
+
+  @sa check_and_update_table_version()
+  @sa Execute_observer
+  @sa Prepared_statement::reprepare()
+*/
+
+enum enum_table_ref_type
+{
+  /** Initial value set by the parser */
+  TABLE_REF_NULL= 0,
+  TABLE_REF_VIEW,
+  TABLE_REF_BASE_TABLE,
+  TABLE_REF_I_S_TABLE,
+  TABLE_REF_TMP_TABLE
+};
+
 /*
   External variables
 */

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2008-05-14 13:49:41 +0000
+++ b/sql/mysqld.cc	2008-05-21 10:17:29 +0000
@@ -2976,6 +2976,7 @@
       by the stored procedures code.
     */
     if (!thd->is_fatal_error && thd->spcont &&
+        ! (MyFlags & ME_NO_SP_HANDLER) &&
         thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
     {
       /*
@@ -2985,7 +2986,8 @@
       DBUG_VOID_RETURN;
     }
 
-    if (!thd->no_warnings_for_error && !thd->is_fatal_error)
+    if (!thd->is_fatal_error && !thd->no_warnings_for_error &&
+        !(MyFlags & ME_NO_WARNING_FOR_ERROR))
     {
       /*
         Suppress infinite recursion if there a memory allocation error
@@ -3227,6 +3229,7 @@
   {"stmt_execute",         (char*) offsetof(STATUS_VAR, com_stmt_execute),
SHOW_LONG_STATUS},
   {"stmt_fetch",           (char*) offsetof(STATUS_VAR, com_stmt_fetch),
SHOW_LONG_STATUS},
   {"stmt_prepare",         (char*) offsetof(STATUS_VAR, com_stmt_prepare),
SHOW_LONG_STATUS},
+  {"stmt_reprepare",       (char*) offsetof(STATUS_VAR, com_stmt_reprepare),
SHOW_LONG_STATUS},
   {"stmt_reset",           (char*) offsetof(STATUS_VAR, com_stmt_reset),
SHOW_LONG_STATUS},
   {"stmt_send_long_data",  (char*) offsetof(STATUS_VAR, com_stmt_send_long_data),
SHOW_LONG_STATUS},
   {"truncate",             (char*) offsetof(STATUS_VAR, com_stat[(uint)
SQLCOM_TRUNCATE]), SHOW_LONG_STATUS},
@@ -3315,7 +3318,7 @@
     We have few debug-only commands in com_status_vars, only visible in debug
     builds. for simplicity we enable the assert only in debug builds
 
-    There are 7 Com_ variables which don't have corresponding SQLCOM_ values:
+    There are 8 Com_ variables which don't have corresponding SQLCOM_ values:
     (TODO strictly speaking they shouldn't be here, should not have Com_ prefix
     that is. Perhaps Stmt_ ? Comstmt_ ? Prepstmt_ ?)
 
@@ -3324,6 +3327,7 @@
       Com_stmt_execute         => com_stmt_execute
       Com_stmt_fetch           => com_stmt_fetch
       Com_stmt_prepare         => com_stmt_prepare
+      Com_stmt_reprepare       => com_stmt_reprepare
       Com_stmt_reset           => com_stmt_reset
       Com_stmt_send_long_data  => com_stmt_send_long_data
 
@@ -3332,7 +3336,7 @@
     of SQLCOM_ constants.
   */
   compile_time_assert(sizeof(com_status_vars)/sizeof(com_status_vars[0]) - 1 ==
-                     SQLCOM_END + 7);
+                     SQLCOM_END + 8);
 #endif
 
   load_defaults(conf_file_name, groups, &argc, &argv);
@@ -6890,7 +6894,8 @@
   {"table_definition_cache", OPT_TABLE_DEF_CACHE,
    "The number of cached table definitions.",
    (uchar**) &table_def_size, (uchar**) &table_def_size,
-   0, GET_ULONG, REQUIRED_ARG, 128, 1, 512*1024L, 0, 1, 0},
+   0, GET_ULONG, REQUIRED_ARG, TABLE_DEF_CACHE_DEFAULT, TABLE_DEF_CACHE_MIN,
+   512*1024L, 0, 1, 0},
   {"table_open_cache", OPT_TABLE_OPEN_CACHE,
    "The number of cached open tables.",
    (uchar**) &table_cache_size, (uchar**) &table_cache_size, 0, GET_ULONG,

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2008-04-30 23:34:30 +0000
+++ b/sql/share/errmsg.txt	2008-05-21 10:17:29 +0000
@@ -6126,6 +6126,8 @@
 
 ER_LOG_PURGE_NO_FILE  
 	eng "Being purged log %s was not found"
+ER_NEED_REPREPARE
+  eng "Prepared statement needs to be re-prepared"
 ER_WARN_AUTO_CONVERT_LOCK
         eng "Converted to non-transactional lock on '%-.64s'"
         ger "Umgewandelt zu nicht-transaktionalen Sperren auf '%-.64s'"

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2008-05-16 21:34:48 +0000
+++ b/sql/sp_head.cc	2008-05-21 10:17:29 +0000
@@ -1066,6 +1066,7 @@
   LEX *old_lex;
   Item_change_list old_change_list;
   String old_packet;
+  Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
 
   Object_creation_ctx *saved_creation_ctx;
 
@@ -1133,6 +1134,25 @@
   thd->variables.sql_mode= m_sql_mode;
   save_abort_on_warning= thd->abort_on_warning;
   thd->abort_on_warning= 0;
+  /**
+    When inside a substatement (a stored function or trigger
+    statement), clear the metadata observer in THD, if any.
+    Remember the value of the observer here, to be able
+    to restore it when leaving the substatement.
+
+    We reset the observer to suppress errors when a substatement
+    uses temporary tables. If a temporary table does not exist
+    at start of the main statement, it's not prelocked
+    and thus is not validated with other prelocked tables.
+
+    Later on, when the temporary table is opened, metadata
+    versions mismatch, expectedly.
+
+    The proper solution for the problem is to re-validate tables
+    of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
+    but it's not implemented yet.
+  */
+  thd->m_reprepare_observer= 0;
 
   /*
     It is also more efficient to save/restore current thd->lex once when
@@ -1295,6 +1315,7 @@
   thd->derived_tables= old_derived_tables;
   thd->variables.sql_mode= save_sql_mode;
   thd->abort_on_warning= save_abort_on_warning;
+  thd->m_reprepare_observer= save_reprepare_observer;
 
   thd->stmt_arena= old_arena;
   state= EXECUTED;

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2008-05-16 10:30:46 +0000
+++ b/sql/sql_base.cc	2008-05-21 10:17:29 +0000
@@ -3718,6 +3718,73 @@
   DBUG_VOID_RETURN;
 }
 
+
+/**
+  Compare metadata versions of an element obtained from the table
+  definition cache and its corresponding node in the parse tree.
+
+  @details If the new and the old values mismatch, invoke
+  Metadata_version_observer.
+  At prepared statement prepare, all TABLE_LIST version values are
+  NULL and we always have a mismatch. But there is no observer set
+  in THD, and therefore no error is reported. Instead, we update
+  the value in the parse tree, effectively recording the original
+  version.
+  At prepared statement execute, an observer may be installed.  If
+  there is a version mismatch, we push an error and return TRUE.
+
+  For conventional execution (no prepared statements), the
+  observer is never installed.
+
+  @sa Execute_observer
+  @sa check_prepared_statement() to see cases when an observer is installed
+  @sa TABLE_LIST::is_table_ref_id_equal()
+  @sa TABLE_SHARE::get_table_ref_id()
+
+  @param[in]      thd         used to report errors
+  @param[in,out]  tables      TABLE_LIST instance created by the parser
+                              Metadata version information in this object
+                              is updated upon success.
+  @param[in]      table_share an element from the table definition cache
+
+  @retval  TRUE  an error, which has been reported
+  @retval  FALSE success, version in TABLE_LIST has been updated
+*/
+
+bool
+check_and_update_table_version(THD *thd,
+                               TABLE_LIST *tables, TABLE_SHARE *table_share)
+{
+  if (! tables->is_table_ref_id_equal(table_share))
+  {
+    if (thd->m_reprepare_observer &&
+        thd->m_reprepare_observer->report_error(thd))
+    {
+      /*
+        Version of the table share is different from the
+        previous execution of the prepared statement, and it is
+        unacceptable for this SQLCOM. Error has been reported.
+      */
+      DBUG_ASSERT(thd->is_error());
+      return TRUE;
+    }
+    /* Always maintain the latest version and type */
+    tables->set_table_ref_id(table_share);
+  }
+
+#ifndef DBUG_OFF
+  /* Spuriously reprepare each statement. */
+  if (_db_strict_keyword_("reprepare_each_statement") &&
+      thd->m_reprepare_observer && thd->stmt_arena->is_reprepared ==
FALSE)
+  {
+    thd->m_reprepare_observer->report_error(thd);
+    return TRUE;
+  }
+#endif
+
+  return FALSE;
+}
+
 /*
   Load a table definition from file and open unireg table
 
@@ -3763,6 +3830,12 @@
 
   if (share->is_view)
   {
+    /*
+      This table is a view. Validate its metadata version: in particular,
+      that it was a view when the statement was prepared.
+    */
+    if (check_and_update_table_version(thd, table_list, share))
+      goto err;
     if (table_list->i_s_requested_object &  OPEN_TABLE_ONLY)
       goto err;
 
@@ -3780,6 +3853,26 @@
     release_table_share(share, RELEASE_NORMAL);
     DBUG_RETURN((flags & OPEN_VIEW_NO_PARSE)? -1 : 0);
   }
+  else if (table_list->view)
+  {
+    /*
+      We're trying to open a table for what was a view.
+      This can only happen during (re-)execution.
+      At prepared statement prepare the view has been opened and
+      merged into the statement parse tree. After that, someone
+      performed a DDL and replaced the view with a base table.
+      Don't try to open the table inside a prepared statement,
+      invalidate it instead.
+
+      Note, the assert below is known to fail inside stored
+      procedures (Bug#27011).
+    */
+    DBUG_ASSERT(thd->m_reprepare_observer);
+    check_and_update_table_version(thd, table_list, share);
+    /* Always an error. */
+    DBUG_ASSERT(thd->is_error());
+    goto err;
+  }
 
   if (table_list->i_s_requested_object &  OPEN_VIEW_ONLY)
     goto err;
@@ -4381,8 +4474,18 @@
     */
     if (tables->schema_table)
     {
-      if (!mysql_schema_table(thd, thd->lex, tables))
+      /*
+        If this information_schema table is merged into a mergeable
+        view, ignore it for now -- it will be filled when its respective
+        TABLE_LIST is processed. This code works only during re-execution.
+      */
+      if (tables->view)
+        goto process_view_routines;
+      if (!mysql_schema_table(thd, thd->lex, tables) &&
+          !check_and_update_table_version(thd, tables, tables->table->s))
+      {
         continue;
+      }
       DBUG_RETURN(-1);
     }
     (*counter)++;
@@ -4530,6 +4633,13 @@
     }
     tables->table->grant= tables->grant;
 
+    /* Check and update metadata version of a base table. */
+    if (check_and_update_table_version(thd, tables, tables->table->s))
+    {
+      result= -1;
+      goto err;
+    }
+
     /* Attach MERGE children if not locked already. */
     DBUG_PRINT("tcache", ("is parent: %d  is child: %d",
                           test(tables->table->child_l),
@@ -4588,7 +4698,11 @@
       error happens on a MERGE child, clear the parents TABLE reference.
     */
     if (tables->parent_l)
+    {
+      if (tables->parent_l->next_global ==
tables->parent_l->table->child_l)
+        tables->parent_l->next_global=
*tables->parent_l->table->child_last_l;
       tables->parent_l->table= NULL;
+    }
     tables->table= NULL;
   }
   DBUG_PRINT("tcache", ("returning: %d", result));

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2008-05-14 13:49:41 +0000
+++ b/sql/sql_class.cc	2008-05-21 10:17:29 +0000
@@ -201,6 +201,19 @@
 ** Thread specific functions
 ****************************************************************************/
 
+/** Push an error to the error stack and return TRUE for now. */
+
+bool
+Reprepare_observer::report_error(THD *thd)
+{
+  my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
+
+  m_invalidated= TRUE;
+
+  return TRUE;
+}
+
+
 Open_tables_state::Open_tables_state(ulong version_arg)
   :version(version_arg), state_flags(0U)
 {
@@ -2834,7 +2847,8 @@
   DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
               handler_tables == 0 && derived_tables == 0 &&
               lock == 0 && locked_tables == 0 &&
-              prelocked_mode == NON_PRELOCKED);
+              prelocked_mode == NON_PRELOCKED &&
+              m_reprepare_observer == NULL);
   set_open_tables_state(backup);
   DBUG_VOID_RETURN;
 }

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2008-05-13 15:15:40 +0000
+++ b/sql/sql_class.h	2008-05-21 10:17:29 +0000
@@ -24,6 +24,51 @@
 #include "log.h"
 #include "rpl_tblmap.h"
 
+/**
+  An interface that is used to take an action when
+  the locking module notices that a table version has changed
+  since the last execution. "Table" here may refer to any kind of
+  table -- a base table, a temporary table, a view or an
+  information schema table.
+
+  When we open and lock tables for execution of a prepared
+  statement, we must verify that they did not change
+  since statement prepare. If some table did change, the statement
+  parse tree *may* be no longer valid, e.g. in case it contains
+  optimizations that depend on table metadata.
+
+  This class provides an interface (a method) that is
+  invoked when such a situation takes place.
+  The implementation of the method simply reports an error, but
+  the exact details depend on the nature of the SQL statement.
+
+  At most 1 instance of this class is active at a time, in which
+  case THD::m_reprepare_observer is not NULL.
+
+  @sa check_and_update_table_version() for details of the
+  version tracking algorithm 
+
+  @sa Open_tables_state::m_reprepare_observer for the life cycle
+  of metadata observers.
+*/
+
+class Reprepare_observer
+{
+public:
+  /**
+    Check if a change of metadata is OK. In future
+    the signature of this method may be extended to accept the old
+    and the new versions, but since currently the check is very
+    simple, we only need the THD to report an error.
+  */
+  bool report_error(THD *thd);
+  bool is_invalidated() const { return m_invalidated; }
+  void reset_reprepare_observer() { m_invalidated= FALSE; }
+private:
+  bool m_invalidated;
+};
+
+
 class Relay_log_info;
 
 class Query_log_event;
@@ -451,6 +496,7 @@
   ulong filesort_scan_count;
   /* Prepared statements and binary protocol */
   ulong com_stmt_prepare;
+  ulong com_stmt_reprepare;
   ulong com_stmt_execute;
   ulong com_stmt_send_long_data;
   ulong com_stmt_fetch;
@@ -490,7 +536,7 @@
 
 /* The following macro is to make init of Query_arena simpler */
 #ifndef DBUG_OFF
-#define INIT_ARENA_DBUG_INFO is_backup_arena= 0
+#define INIT_ARENA_DBUG_INFO is_backup_arena= 0; is_reprepared= FALSE;
 #else
 #define INIT_ARENA_DBUG_INFO
 #endif
@@ -506,6 +552,7 @@
   MEM_ROOT *mem_root;                   // Pointer to current memroot
 #ifndef DBUG_OFF
   bool is_backup_arena; /* True if this arena is used for backup. */
+  bool is_reprepared;
 #endif
   /*
     The states relfects three diffrent life cycles for three
@@ -843,6 +890,20 @@
 {
 public:
   /**
+    As part of class THD, this member is set during execution
+    of a prepared statement. When it is set, it is used
+    by the locking subsystem to report a change in table metadata.
+
+    When Open_tables_state part of THD is reset to open
+    a system or INFORMATION_SCHEMA table, the member is cleared
+    to avoid spurious ER_NEED_REPREPARE errors -- system and
+    INFORMATION_SCHEMA tables are not subject to metadata version
+    tracking.
+    @sa check_and_update_table_version()
+  */
+  Reprepare_observer *m_reprepare_observer;
+
+  /**
     List of regular tables in use by this thread. Contains temporary and
     base tables that were opened with @see open_tables().
   */
@@ -945,6 +1006,7 @@
     extra_lock= lock= locked_tables= 0;
     prelocked_mode= NON_PRELOCKED;
     state_flags= 0U;
+    m_reprepare_observer= NULL;
   }
 };
 
@@ -2886,6 +2948,20 @@
 #define CF_STATUS_COMMAND	4
 #define CF_SHOW_TABLE_COMMAND	8
 #define CF_WRITE_LOGS_COMMAND  16
+/**
+  Must be set for SQL statements that may contain
+  Item expressions and/or use joins and tables.
+  Indicates that the parse tree of such statement may
+  contain rule-based optimizations that depend on metadata
+  (i.e. number of columns in a table), and consequently
+  that the statement must be re-prepared whenever
+  referenced metadata changes. Must not be set for
+  statements that themselves change metadata, e.g. RENAME,
+  ALTER and other DDL, since otherwise will trigger constant
+  reprepare. Consequently, complex item expressions and
+  joins are currently prohibited in these statements.
+*/
+#define CF_REEXECUTION_FRAGILE 32
 
 /* Functions in sql_class.cc */
 

=== modified file 'sql/sql_cursor.cc'
--- a/sql/sql_cursor.cc	2008-02-21 01:37:58 +0000
+++ b/sql/sql_cursor.cc	2008-05-21 10:17:29 +0000
@@ -111,7 +111,8 @@
   select_result *result; /**< the result object of the caller (PS or SP) */
 public:
   Materialized_cursor *materialized_cursor;
-  Select_materialize(select_result *result_arg) :result(result_arg) {}
+  Select_materialize(select_result *result_arg)
+    :result(result_arg), materialized_cursor(0) {}
   virtual bool send_fields(List<Item> &list, uint flags);
 };
 

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-05-16 04:29:09 +0000
+++ b/sql/sql_parse.cc	2008-05-21 10:17:29 +0000
@@ -209,44 +209,55 @@
 {
   bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags));
 
-  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA;
+  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_CREATE_INDEX]=   CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
   sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND;
   sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA;
-  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA;
+  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_CREATE_DB]=      CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_DROP_DB]=        CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_RENAME_TABLE]=   CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_RESTORE]=        CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_DROP_INDEX]=     CF_CHANGES_DATA;
-  sql_command_flags[SQLCOM_CREATE_VIEW]=    CF_CHANGES_DATA;
+  sql_command_flags[SQLCOM_CREATE_VIEW]=    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_DROP_VIEW]=      CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_CREATE_EVENT]=   CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_ALTER_EVENT]=    CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_DROP_EVENT]=     CF_CHANGES_DATA;
 
-  sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
-  sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
+  sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
+                                            CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SELECT]=         CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SET_OPTION]=     CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_DO]=             CF_REEXECUTION_FRAGILE;
 
-  sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_STATUS]=      CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_DATABASES]=   CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_TRIGGERS]=    CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_EVENTS]=      CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND;
+  sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_STATUS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_DATABASES]=   CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_TRIGGERS]=    CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_EVENTS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_SHOW_PLUGINS]=     CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_FIELDS]=      CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_KEYS]=        CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_VARIABLES]=   CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_CHARSETS]=    CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_COLLATIONS]=  CF_STATUS_COMMAND;
+  sql_command_flags[SQLCOM_SHOW_FIELDS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_KEYS]=        CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_VARIABLES]=   CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_CHARSETS]=    CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
+  sql_command_flags[SQLCOM_SHOW_COLLATIONS]=  CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND;
@@ -270,7 +281,7 @@
   sql_command_flags[SQLCOM_SHOW_CREATE_PROC]=  CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]=  CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]=  CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]=  CF_STATUS_COMMAND;
+  sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]=  CF_STATUS_COMMAND |
CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_SHOW_PROC_CODE]=  CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_FUNC_CODE]=  CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]=  CF_STATUS_COMMAND;
@@ -278,9 +289,11 @@
   sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
 
    sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
-                                               CF_SHOW_TABLE_COMMAND);
+                                                 CF_SHOW_TABLE_COMMAND |
+                                                 CF_REEXECUTION_FRAGILE);
   sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
-                                                CF_SHOW_TABLE_COMMAND);
+                                                CF_SHOW_TABLE_COMMAND |
+                                                CF_REEXECUTION_FRAGILE);
 
   /*
     The following is used to preserver CF_ROW_COUNT during the
@@ -288,7 +301,7 @@
     last called (or executed) statement is preserved.
     See mysql_execute_command() for how CF_ROW_COUNT is used.
   */
-  sql_command_flags[SQLCOM_CALL]= 		CF_HAS_ROW_COUNT;
+  sql_command_flags[SQLCOM_CALL]= 		CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE;
   sql_command_flags[SQLCOM_EXECUTE]= 		CF_HAS_ROW_COUNT;
 
   /*
@@ -857,8 +870,11 @@
   /* TODO: set thd->lex->sql_command to SQLCOM_END here */
   pthread_mutex_unlock(&LOCK_thread_count);
 
-  thd->server_status&=
-           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
+  /**
+    Clear the set of flags that are expected to be cleared at the
+    beginning of each command.
+  */
+  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
   switch (command) {
   case COM_INIT_DB:
   {
@@ -5501,9 +5517,11 @@
 
   thd->query_start_used= 0;
   thd->is_fatal_error= thd->time_zone_used= 0;
-  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
-                          SERVER_QUERY_NO_INDEX_USED |
-                          SERVER_QUERY_NO_GOOD_INDEX_USED);
+  /*
+    Clear the status flag that are expected to be cleared at the
+    beginning of each SQL statement.
+  */
+  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
   /*
     If in autocommit mode and not in a transaction, reset
     OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2008-05-08 16:01:15 +0000
+++ b/sql/sql_prepare.cc	2008-05-21 19:44:56 +0000
@@ -156,20 +156,27 @@
   bool set_name(LEX_STRING *name);
   inline void close_cursor() { delete cursor; cursor= 0; }
   inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
+  inline bool is_protocol_text() const { return protocol == &thd->protocol_text; }
   bool prepare(const char *packet, uint packet_length);
-  bool execute(String *expanded_query, bool open_cursor);
+  bool execute_loop(String *expanded_query,
+                    bool open_cursor,
+                    uchar *packet_arg, uchar *packet_end_arg);
   /* Destroy this statement */
   void deallocate();
 private:
   /**
-    Store the parsed tree of a prepared statement here.
-  */
-  LEX main_lex;
-  /**
     The memory root to allocate parsed tree elements (instances of Item,
     SELECT_LEX and other classes).
   */
   MEM_ROOT main_mem_root;
+private:
+  bool set_db(const char *db, uint db_length);
+  bool set_parameters(String *expanded_query,
+                      uchar *packet, uchar *packet_end);
+  bool execute(String *expanded_query, bool open_cursor);
+  bool reprepare();
+  bool validate_metadata(Prepared_statement  *copy);
+  void swap_prepared_statement(Prepared_statement *copy);
 };
 
 
@@ -941,6 +948,55 @@
 
 #endif /*!EMBEDDED_LIBRARY*/
 
+/**
+  Setup data conversion routines using an array of parameter
+  markers from the original prepared statement.
+  Swap the parameter data of the original prepared
+  statement to the new one.
+
+  Used only when we re-prepare a prepared statement.
+  There are two reasons for this function to exist:
+
+  1) In the binary client/server protocol, parameter metadata
+  is sent only at first execute. Consequently, if we need to
+  reprepare a prepared statement at a subsequent execution,
+  we may not have metadata information in the packet.
+  In that case we use the parameter array of the original
+  prepared statement to setup parameter types of the new
+  prepared statement.
+
+  2) In the binary client/server protocol, we may supply
+  long data in pieces. When the last piece is supplied,
+  we assemble the pieces and convert them from client
+  character set to the connection character set. After
+  that the parameter value is only available inside
+  the parameter, the original pieces are lost, and thus
+  we can only assign the corresponding parameter of the
+  reprepared statement from the original value.
+
+  @param[out]  param_array_dst  parameter markers of the new statement
+  @param[in]   param_array_src  parameter markers of the original
+                                statement
+  @param[in]   param_count      total number of parameters. Is the
+                                same in src and dst arrays, since
+                                the statement query is the same
+
+  @return this function never fails
+*/
+
+static void
+swap_parameter_array(Item_param **param_array_dst,
+                     Item_param **param_array_src,
+                     uint param_count)
+{
+  Item_param **dst= param_array_dst;
+  Item_param **src= param_array_src;
+  Item_param **end= param_array_dst + param_count;
+
+  for (; dst < end; ++src, ++dst)
+    (*dst)->set_param_type_and_swap_value(*src);
+}
+
 
 /**
   Assign prepared statement parameters from user variables.
@@ -1264,7 +1320,7 @@
 */
 
 static int mysql_test_select(Prepared_statement *stmt,
-                             TABLE_LIST *tables, bool text_protocol)
+                             TABLE_LIST *tables)
 {
   THD *thd= stmt->thd;
   LEX *lex= stmt->lex;
@@ -1301,7 +1357,7 @@
   */
   if (unit->prepare(thd, 0, 0))
     goto error;
-  if (!lex->describe && !text_protocol)
+  if (!lex->describe && !stmt->is_protocol_text())
   {
     /* Make copy of item list, as change_columns may change it */
     List<Item> fields(lex->select_lex.item_list);
@@ -1393,6 +1449,44 @@
 
 
 /**
+  Validate and prepare for execution CALL statement expressions.
+
+  @param stmt               prepared statement
+  @param tables             list of tables used in this query
+  @param value_list         list of expressions
+
+  @retval FALSE             success
+  @retval TRUE              error, error message is set in THD
+*/
+
+static bool mysql_test_call_fields(Prepared_statement *stmt,
+                                   TABLE_LIST *tables,
+                                   List<Item> *value_list)
+{
+  DBUG_ENTER("mysql_test_call_fields");
+
+  List_iterator<Item> it(*value_list);
+  THD *thd= stmt->thd;
+  Item *item;
+
+  if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, FALSE,
+                                   UINT_MAX) ||
+      open_normal_and_derived_tables(thd, tables, 0))
+    goto err;
+
+  while ((item= it++))
+  {
+    if (!item->fixed && item->fix_fields(thd, it.ref()) ||
+        item->check_cols(1))
+      goto err;
+  }
+  DBUG_RETURN(FALSE);
+err:
+  DBUG_RETURN(TRUE);
+}
+
+
+/**
   Check internal SELECT of the prepared command.
 
   @param stmt                      prepared statement
@@ -1513,6 +1607,17 @@
 
     res= select_like_stmt_test(stmt, 0, 0);
   }
+  else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+  {
+    /*
+      Check that the source table exist, and also record
+      its metadata version. Even though not strictly necessary,
+      we validate metadata of all CREATE TABLE statements,
+      which keeps metadata validation code simple.
+    */
+    if (open_normal_and_derived_tables(stmt->thd, lex->query_tables, 0))
+      DBUG_RETURN(TRUE);
+  }
 
   /* put tables back for PS rexecuting */
   lex->link_first_table_back(create_table, link_to_local);
@@ -1709,8 +1814,7 @@
     TRUE              error, error message is set in THD (but not sent)
 */
 
-static bool check_prepared_statement(Prepared_statement *stmt,
-                                     bool text_protocol)
+static bool check_prepared_statement(Prepared_statement *stmt)
 {
   THD *thd= stmt->thd;
   LEX *lex= stmt->lex;
@@ -1755,9 +1859,23 @@
   case SQLCOM_DELETE:
     res= mysql_test_delete(stmt, tables);
     break;
-
+  /* The following allow WHERE clause, so they must be tested like SELECT */
+  case SQLCOM_SHOW_DATABASES:
+  case SQLCOM_SHOW_TABLES:
+  case SQLCOM_SHOW_TRIGGERS:
+  case SQLCOM_SHOW_EVENTS:
+  case SQLCOM_SHOW_OPEN_TABLES:
+  case SQLCOM_SHOW_FIELDS:
+  case SQLCOM_SHOW_KEYS:
+  case SQLCOM_SHOW_COLLATIONS:
+  case SQLCOM_SHOW_CHARSETS:
+  case SQLCOM_SHOW_VARIABLES:
+  case SQLCOM_SHOW_STATUS:
+  case SQLCOM_SHOW_TABLE_STATUS:
+  case SQLCOM_SHOW_STATUS_PROC:
+  case SQLCOM_SHOW_STATUS_FUNC:
   case SQLCOM_SELECT:
-    res= mysql_test_select(stmt, tables, text_protocol);
+    res= mysql_test_select(stmt, tables);
     if (res == 2)
     {
       /* Statement and field info has already been sent */
@@ -1780,6 +1898,9 @@
     res= mysql_test_do_fields(stmt, tables, lex->insert_list);
     break;
 
+  case SQLCOM_CALL:
+    res= mysql_test_call_fields(stmt, tables, &lex->value_list);
+    break;
   case SQLCOM_SET_OPTION:
     res= mysql_test_set_fields(stmt, tables, &lex->var_list);
     break;
@@ -1829,7 +1950,6 @@
   case SQLCOM_DROP_INDEX:
   case SQLCOM_ROLLBACK:
   case SQLCOM_TRUNCATE:
-  case SQLCOM_CALL:
   case SQLCOM_DROP_VIEW:
   case SQLCOM_REPAIR:
   case SQLCOM_ANALYZE:
@@ -1872,8 +1992,8 @@
     break;
   }
   if (res == 0)
-    DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) ||
-                                        thd->protocol->flush()));
+    DBUG_RETURN(stmt->is_protocol_text() ?
+                FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
 error:
   DBUG_RETURN(TRUE);
 }
@@ -2312,11 +2432,9 @@
   ulong flags= (ulong) packet[4];
   /* Query text for binary, general or slow log, if any of them is open */
   String expanded_query;
-#ifndef EMBEDDED_LIBRARY
   uchar *packet_end= packet + packet_length;
-#endif
   Prepared_statement *stmt;
-  bool error;
+  bool open_cursor;
   DBUG_ENTER("mysql_stmt_execute");
 
   packet+= 9;                               /* stmt_id + 5 bytes of flags */
@@ -2341,44 +2459,12 @@
   sp_cache_flush_obsolete(&thd->sp_proc_cache);
   sp_cache_flush_obsolete(&thd->sp_func_cache);
 
-#ifndef EMBEDDED_LIBRARY
-  if (stmt->param_count)
-  {
-    uchar *null_array= packet;
-    if (setup_conversion_functions(stmt, &packet, packet_end) ||
-        stmt->set_params(stmt, null_array, packet, packet_end,
-                         &expanded_query))
-      goto set_params_data_err;
-  }
-#else
-  /*
-    In embedded library we re-install conversion routines each time
-    we set params, and also we don't need to parse packet.
-    So we do it in one function.
-  */
-  if (stmt->param_count && stmt->set_params_data(stmt,
&expanded_query))
-    goto set_params_data_err;
-#endif
-  if (!(specialflag & SPECIAL_NO_PRIOR))
-    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
-
-  /*
-    If the free_list is not empty, we'll wrongly free some externally
-    allocated items when cleaning up after validation of the prepared
-    statement.
-  */
-  DBUG_ASSERT(thd->free_list == NULL);
-
-  error= stmt->execute(&expanded_query,
-                       test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
-  if (!(specialflag & SPECIAL_NO_PRIOR))
-    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
-  DBUG_VOID_RETURN;
-
-set_params_data_err:
-  my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
-  reset_stmt_params(stmt);
-  DBUG_VOID_RETURN;
+  open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
+
+  stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
+
+  DBUG_VOID_RETURN;
+
 }
 
 
@@ -2424,24 +2510,8 @@
 
   DBUG_PRINT("info",("stmt: %p", stmt));
 
-  /*
-    If the free_list is not empty, we'll wrongly free some externally
-    allocated items when cleaning up after validation of the prepared
-    statement.
-  */
-  DBUG_ASSERT(thd->free_list == NULL);
-
-  if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params,
-                                 &expanded_query))
-    goto set_params_data_err;
-
-  (void) stmt->execute(&expanded_query, FALSE);
-
-  DBUG_VOID_RETURN;
-
-set_params_data_err:
-  my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
-  reset_stmt_params(stmt);
+  (void) stmt->execute_loop(&expanded_query, FALSE, NULL, NULL);
+
   DBUG_VOID_RETURN;
 }
 
@@ -2745,7 +2815,7 @@
 ****************************************************************************/
 
 Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
-  :Statement(&main_lex, &main_mem_root,
+  :Statement(NULL, &main_mem_root,
              INITIALIZED, ++thd_arg->statement_id_counter),
   thd(thd_arg),
   result(thd_arg),
@@ -2816,7 +2886,11 @@
     like Item_param, don't free everything until free_items()
   */
   free_items();
-  delete lex->result;
+  if (lex)
+  {
+    delete lex->result;
+    delete (st_lex_local *) lex;
+  }
   free_root(&main_mem_root, MYF(0));
   DBUG_VOID_RETURN;
 }
@@ -2852,6 +2926,34 @@
   return name.str == 0;
 }
 
+
+/**
+  Remember the current database.
+
+  We must reset/restore the current database during execution of
+  a prepared statement since it affects execution environment:
+  privileges, @@character_set_database, and other.
+
+  @return Returns an error if out of memory.
+*/
+
+bool
+Prepared_statement::set_db(const char *db_arg, uint db_length_arg)
+{
+  /* Remember the current database. */
+  if (db_arg && db_length_arg)
+  {
+    db= this->strmake(db_arg, db_length_arg);
+    db_length= db_length_arg;
+  }
+  else
+  {
+    db= NULL;
+    db_length= 0;
+  }
+  return db_arg != NULL && db == NULL;
+}
+
 /**************************************************************************
   Common parts of mysql_[sql]_stmt_prepare, mysql_[sql]_stmt_execute.
   Essentially, these functions do all the magic of preparing/executing
@@ -2893,6 +2995,12 @@
   */
   status_var_increment(thd->status_var.com_stmt_prepare);
 
+  if (! (lex= new (mem_root) st_lex_local))
+    DBUG_RETURN(TRUE);
+
+  if (set_db(thd->db, thd->db_length))
+    DBUG_RETURN(TRUE);
+
   /*
     alloc_query() uses thd->memroot && thd->query, so we should call
     both of backup_statement() and backup_query_arena() here.
@@ -2920,19 +3028,6 @@
 
   lex->set_trg_event_type_for_tables();
 
-  /* Remember the current database. */
-
-  if (thd->db && thd->db_length)
-  {
-    db= this->strmake(thd->db, thd->db_length);
-    db_length= thd->db_length;
-  }
-  else
-  {
-    db= NULL;
-    db_length= 0;
-  }
-
   /*
     While doing context analysis of the query (in check_prepared_statement)
     we allocate a lot of additional memory: for open tables, JOINs, derived
@@ -2956,7 +3051,7 @@
   */
 
   if (error == 0)
-    error= check_prepared_statement(this, name.str != 0);
+    error= check_prepared_statement(this);
 
   /*
     Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared
@@ -3003,6 +3098,305 @@
   DBUG_RETURN(error);
 }
 
+
+/**
+  Assign parameter values either from variables, in case of SQL PS
+  or from the execute packet.
+
+  @param expanded_query  a container with the original SQL statement.
+                         '?' placeholders will be replaced with
+                         their values in case of success.
+                         The result is used for logging and replication
+  @param packet          pointer to execute packet.
+                         NULL in case of SQL PS
+  @param packet_end      end of the packet. NULL in case of SQL PS
+
+  @todo Use a paremeter source class family instead of 'if's, and
+  support stored procedure variables.
+
+  @retval TRUE an error occurred when assigning a parameter (likely
+          a conversion error or out of memory, or malformed packet)
+  @retval FALSE success
+*/
+
+bool
+Prepared_statement::set_parameters(String *expanded_query,
+                                   uchar *packet, uchar *packet_end)
+{
+  bool is_sql_ps= packet == NULL;
+  bool res= FALSE;
+
+  if (is_sql_ps)
+  {
+    /* SQL prepared statement */
+    res= set_params_from_vars(this, thd->lex->prepared_stmt_params,
+                              expanded_query);
+  }
+  else if (param_count)
+  {
+#ifndef EMBEDDED_LIBRARY
+    uchar *null_array= packet;
+    res= (setup_conversion_functions(this, &packet, packet_end) ||
+          set_params(this, null_array, packet, packet_end, expanded_query));
+#else
+    /*
+      In embedded library we re-install conversion routines each time
+      we set parameters, and also we don't need to parse packet.
+      So we do it in one function.
+    */
+    res= set_params_data(this, expanded_query);
+#endif
+  }
+  if (res)
+  {
+    my_error(ER_WRONG_ARGUMENTS, MYF(0),
+             is_sql_ps ? "EXECUTE" : "mysql_stmt_execute");
+    reset_stmt_params(this);
+  }
+  return res;
+}
+
+
+/**
+  Execute a prepared statement. Re-prepare it a limited number
+  of times if necessary.
+
+  Try to execute a prepared statement. If there is a metadata
+  validation error, prepare a new copy of the prepared statement,
+  swap the old and the new statements, and try again.
+  If there is a validation error again, repeat the above, but
+  perform no more than MAX_REPREPARE_ATTEMPTS.
+
+  @note We have to try several times in a loop since we
+  release metadata locks on tables after prepared statement
+  prepare. Therefore, a DDL statement may sneak in between prepare
+  and execute of a new statement. If this happens repeatedly
+  more than MAX_REPREPARE_ATTEMPTS times, we give up.
+
+  In future we need to be able to keep the metadata locks between
+  prepare and execute, but right now open_and_lock_tables(), as
+  well as close_thread_tables() are buried deep inside
+  execution code (mysql_execute_command()).
+
+  @return TRUE if an error, FALSE if success
+  @retval  TRUE    either MAX_REPREPARE_ATTEMPTS has been reached,
+                   or some general error
+  @retval  FALSE   successfully executed the statement, perhaps
+                   after having reprepared it a few times.
+*/
+
+bool
+Prepared_statement::execute_loop(String *expanded_query,
+                                 bool open_cursor,
+                                 uchar *packet,
+                                 uchar *packet_end)
+{
+  const int MAX_REPREPARE_ATTEMPTS= 3;
+  Reprepare_observer reprepare_observer;
+  bool error;
+  int reprepare_attempt= 0;
+
+  if (set_parameters(expanded_query, packet, packet_end))
+    return TRUE;
+
+reexecute:
+  reprepare_observer.reset_reprepare_observer();
+
+  /*
+    If the free_list is not empty, we'll wrongly free some externally
+    allocated items when cleaning up after validation of the prepared
+    statement.
+  */
+  DBUG_ASSERT(thd->free_list == NULL);
+
+  /*
+    Install the metadata observer. If some metadata version is
+    different from prepare time and an observer is installed,
+    the observer method will be invoked to push an error into
+    the error stack.
+  */
+  if (sql_command_flags[lex->sql_command] &
+      CF_REEXECUTION_FRAGILE)
+  {
+    DBUG_ASSERT(thd->m_reprepare_observer == NULL);
+    thd->m_reprepare_observer = &reprepare_observer;
+  }
+
+  if (!(specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+
+  error= execute(expanded_query, open_cursor) || thd->is_error();
+
+  if (!(specialflag & SPECIAL_NO_PRIOR))
+    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+
+  thd->m_reprepare_observer= NULL;
+
+  if (error && !thd->is_fatal_error && !thd->killed &&
+      reprepare_observer.is_invalidated() &&
+      reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
+  {
+    DBUG_ASSERT(thd->main_da.sql_errno() == ER_NEED_REPREPARE);
+    thd->clear_error();
+
+    error= reprepare();
+
+    if (! error)                                /* Success */
+      goto reexecute;
+  }
+  reset_stmt_params(this);
+
+  return error;
+}
+
+
+/**
+  Reprepare this prepared statement.
+
+  Currently this is implemented by creating a new prepared
+  statement, preparing it with the original query and then
+  swapping the new statement and the original one.
+
+  @retval  TRUE   an error occurred. Possible errors include
+                  incompatibility of new and old result set
+                  metadata
+  @retval  FALSE  success, the statement has been reprepared
+*/
+
+bool
+Prepared_statement::reprepare()
+{
+  char saved_cur_db_name_buf[NAME_LEN+1];
+  LEX_STRING saved_cur_db_name=
+    { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+  LEX_STRING stmt_db_name= { db, db_length };
+  bool cur_db_changed;
+  bool error;
+
+  Prepared_statement copy(thd, &thd->protocol_text);
+
+  status_var_increment(thd->status_var.com_stmt_reprepare);
+
+  if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
+                          &cur_db_changed))
+    return TRUE;
+
+  error= (name.str && copy.set_name(&name) ||
+          copy.prepare(query, query_length) ||
+          validate_metadata(&copy));
+
+  if (cur_db_changed)
+    mysql_change_db(thd, &saved_cur_db_name, TRUE);
+
+  if (! error)
+  {
+    swap_prepared_statement(&copy);
+    swap_parameter_array(param_array, copy.param_array, param_count);
+#ifndef DBUG_OFF
+    is_reprepared= TRUE;
+#endif
+    /*
+      Clear possible warnings during reprepare, it has to be completely
+      transparent to the user. We use mysql_reset_errors() since
+      there were no separate query id issued for re-prepare.
+      Sic: we can't simply silence warnings during reprepare, because if
+      it's failed, we need to return all the warnings to the user.
+    */
+    mysql_reset_errors(thd, TRUE);
+  }
+  return error;
+}
+
+
+/**
+  Validate statement result set metadata (if the statement returns
+  a result set).
+
+  Currently we only check that the number of columns of the result
+  set did not change.
+  This is a helper method used during re-prepare.
+
+  @param[in]  copy  the re-prepared prepared statement to verify
+                    the metadata of
+
+  @retval TRUE  error, ER_PS_REBIND is reported
+  @retval FALSE statement return no or compatible metadata
+*/
+
+
+bool Prepared_statement::validate_metadata(Prepared_statement *copy)
+{
+  /**
+    If this is an SQL prepared statement or EXPLAIN,
+    return FALSE -- the metadata of the original SELECT,
+    if any, has not been sent to the client.
+  */
+  if (is_protocol_text() || lex->describe)
+    return FALSE;
+
+  if (lex->select_lex.item_list.elements !=
+      copy->lex->select_lex.item_list.elements)
+  {
+    /** Column counts mismatch, update the client */
+    thd->server_status|= SERVER_STATUS_METADATA_CHANGED;
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Replace the original prepared statement with a prepared copy.
+
+  This is a private helper that is used as part of statement
+  reprepare
+
+  @return This function does not return any errors.
+*/
+
+void
+Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
+{
+  Statement tmp_stmt;
+
+  /* Swap memory roots. */
+  swap_variables(MEM_ROOT, main_mem_root, copy->main_mem_root);
+
+  /* Swap the arenas */
+  tmp_stmt.set_query_arena(this);
+  set_query_arena(copy);
+  copy->set_query_arena(&tmp_stmt);
+
+  /* Swap the statement parent classes */
+  tmp_stmt.set_statement(this);
+  set_statement(copy);
+  copy->set_statement(&tmp_stmt);
+
+  /* Swap ids back, we need the original id */
+  swap_variables(ulong, id, copy->id);
+  /* Swap mem_roots back, they must continue pointing at the main_mem_roots */
+  swap_variables(MEM_ROOT *, mem_root, copy->mem_root);
+  /*
+    Swap the old and the new parameters array. The old array
+    is allocated in the old arena.
+  */
+  swap_variables(Item_param **, param_array, copy->param_array);
+  /* Swap flags: this is perhaps unnecessary */
+  swap_variables(uint, flags, copy->flags);
+  /* Swap names, the old name is allocated in the wrong memory root */
+  swap_variables(LEX_STRING, name, copy->name);
+  /* Ditto */
+  swap_variables(char *, db, copy->db);
+
+  DBUG_ASSERT(db_length == copy->db_length);
+  DBUG_ASSERT(param_count == copy->param_count);
+  DBUG_ASSERT(thd == copy->thd);
+  last_error[0]= '\0';
+  last_errno= 0;
+  /* Do not swap protocols, the copy always has protocol_text */
+}
+
+
 /**
   Execute a prepared statement.
 
@@ -3165,10 +3559,7 @@
   DBUG_ASSERT(! (error && cursor));
 
   if (! cursor)
-  {
     cleanup_stmt();
-    reset_stmt_params(this);
-  }
 
   thd->set_statement(&stmt_backup);
   thd->stmt_arena= old_stmt_arena;

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2008-05-14 13:49:41 +0000
+++ b/sql/sql_table.cc	2008-05-21 10:17:29 +0000
@@ -4716,9 +4716,6 @@
   DBUG_ENTER("mysql_create_like_table");
 
 
-  /* CREATE TABLE ... LIKE is not allowed for views. */
-  src_table->required_type= FRMTYPE_TABLE;
-
   /*
     By opening source table we guarantee that it exists and no concurrent
     DDL operation will mess with it. Later we also take an exclusive

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2008-05-13 15:15:40 +0000
+++ b/sql/sql_yacc.yy	2008-05-21 10:17:29 +0000
@@ -3734,20 +3734,30 @@
         | LIKE table_ident
           {
             THD *thd= YYTHD;
+            TABLE_LIST *src_table;
             LEX *lex= thd->lex;
 
             lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
-            if (!lex->select_lex.add_table_to_list(thd, $2, NULL, 0, TL_READ))
+            src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
+                                                         TL_READ);
+            if (! src_table)
               MYSQL_YYABORT;
+            /* CREATE TABLE ... LIKE is not allowed for views. */
+            src_table->required_type= FRMTYPE_TABLE;
           }
         | '(' LIKE table_ident ')'
           {
             THD *thd= YYTHD;
+            TABLE_LIST *src_table;
             LEX *lex= thd->lex;
 
             lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
-            if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0, TL_READ))
+            src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
+                                                         TL_READ);
+            if (! src_table)
               MYSQL_YYABORT;
+            /* CREATE TABLE ... LIKE is not allowed for views. */
+            src_table->required_type= FRMTYPE_TABLE;
           }
         ;
 

=== modified file 'sql/table.h'
--- a/sql/table.h	2008-05-08 13:01:30 +0000
+++ b/sql/table.h	2008-05-21 10:17:29 +0000
@@ -444,6 +444,105 @@
     return table_map_id;
   }
 
+  /**
+    Convert unrelated members of TABLE_SHARE to one enum
+    representing its type.
+
+    @todo perhaps we need to have a member instead of a function.
+  */
+  enum enum_table_ref_type get_table_ref_type() const
+  {
+    if (is_view)
+      return TABLE_REF_VIEW;
+    switch (tmp_table) {
+    case NO_TMP_TABLE:
+      return TABLE_REF_BASE_TABLE;
+    case SYSTEM_TMP_TABLE:
+      return TABLE_REF_I_S_TABLE;
+    default:
+      return TABLE_REF_TMP_TABLE;
+    }
+  }
+  /**
+    Return a table metadata version.
+     * for base tables, we return table_map_id.
+       It is assigned from a global counter incremented for each
+       new table loaded into the table definition cache (TDC).
+     * for temporary tables it's table_map_id again. But for
+       temporary tables table_map_id is assigned from
+       thd->query_id. The latter is assigned from a thread local
+       counter incremented for every new SQL statement. Since
+       temporary tables are thread-local, each temporary table
+       gets a unique id.
+     * for everything else (views, information schema tables),
+       the version id is zero.
+
+   This choice of version id is a large compromise
+   to have a working prepared statement validation in 5.1. In
+   future version ids will be persistent, as described in WL#4180.
+
+   Let's try to explain why and how this limited solution allows
+   to validate prepared statements.
+
+   Firstly, sets (in mathematical sense) of version numbers
+   never intersect for different table types. Therefore,
+   version id of a temporary table is never compared with
+   a version id of a view, and vice versa.
+
+   Secondly, for base tables, we know that each DDL flushes the
+   respective share from the TDC. This ensures that whenever
+   a table is altered or dropped and recreated, it gets a new
+   version id.
+   Unfortunately, since elements of the TDC are also flushed on
+   LRU basis, this choice of version ids leads to false positives.
+   E.g. when the TDC size is too small, we may have a SELECT
+   * FROM INFORMATION_SCHEMA.TABLES flush all its elements, which
+   in turn will lead to a validation error and a subsequent
+   reprepare of all prepared statements.  This is
+   considered acceptable, since as long as prepared statements are
+   automatically reprepared, spurious invalidation is only
+   a performance hit. Besides, no better simple solution exists.
+
+   For temporary tables, using thd->query_id ensures that if
+   a temporary table was altered or recreated, a new version id is
+   assigned. This suits validation needs very well and will perhaps
+   never change.
+
+   Metadata of information schema tables never changes.
+   Thus we can safely assume 0 for a good enough version id.
+
+   Views are a special and tricky case. A view is always inlined
+   into the parse tree of a prepared statement at prepare.
+   Thus, when we execute a prepared statement, the parse tree
+   will not get modified even if the view is replaced with another
+   view.  Therefore, we can safely choose 0 for version id of
+   views and effectively never invalidate a prepared statement
+   when a view definition is altered. Note, that this leads to
+   wrong binary log in statement-based replication, since we log
+   prepared statement execution in form Query_log_events
+   containing conventional statements. But since there is no
+   metadata locking for views, the very same problem exists for
+   conventional statements alone, as reported in Bug#25144. The only
+   difference between prepared and conventional execution is,
+   effectively, that for prepared statements the race condition
+   window is much wider.
+   In 6.0 we plan to support view metadata locking (WL#3726) and
+   extend table definition cache to cache views (WL#4298).
+   When this is done, views will be handled in the same fashion
+   as the base tables.
+
+   Finally, by taking into account table type, we always
+   track that a change has taken place when a view is replaced
+   with a base table, a base table is replaced with a temporary
+   table and so on.
+
+   @sa TABLE_LIST::is_table_ref_id_equal()
+  */
+  ulong get_table_ref_version() const
+  {
+    return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
+  }
+
 } TABLE_SHARE;
 
 
@@ -1267,6 +1366,33 @@
     child_def_version= ~0UL;
   }
 
+  /**
+    Compare the version of metadata from the previous execution
+    (if any) with values obtained from the current table
+    definition cache element.
+
+    @sa check_and_update_table_version()
+  */
+  inline
+  bool is_table_ref_id_equal(TABLE_SHARE *s) const
+  {
+    return (m_table_ref_type == s->get_table_ref_type() &&
+            m_table_ref_version == s->get_table_ref_version());
+  }
+
+  /**
+    Record the value of metadata version of the corresponding
+    table definition cache element in this parse tree node.
+
+    @sa check_and_update_table_version()
+  */
+  inline
+  void set_table_ref_id(TABLE_SHARE *s)
+  {
+    m_table_ref_type= s->get_table_ref_type();
+    m_table_ref_version= s->get_table_ref_version();
+  }
+
 private:
   bool prep_check_option(THD *thd, uint8 check_opt_type);
   bool prep_where(THD *thd, Item **conds, bool no_where_clause);
@@ -1277,6 +1403,10 @@
 
   /* Remembered MERGE child def version.  See top comment in ha_myisammrg.cc */
   ulong         child_def_version;
+  /** See comments for set_metadata_id() */
+  enum enum_table_ref_type m_table_ref_type;
+  /** See comments for set_metadata_id() */
+  ulong m_table_ref_version;
 };
 
 class Item;

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2008-04-15 20:29:42 +0000
+++ b/tests/mysql_client_test.c	2008-05-21 10:17:29 +0000
@@ -6668,10 +6668,10 @@
   check_execute_r(stmt, rc); /* unsupported buffer type */
 
   rc= mysql_stmt_store_result(stmt);
-  check_execute(stmt, rc);
+  DIE_UNLESS(rc);
 
   rc= mysql_stmt_store_result(stmt);
-  check_execute_r(stmt, rc); /* commands out of sync */
+  DIE_UNLESS(rc); /* Old error must be reset first */
 
   mysql_stmt_close(stmt);
 
@@ -8423,6 +8423,9 @@
   rc= mysql_stmt_fetch_column(stmt, my_bind, 0, 0);
   check_execute_r(stmt, rc);
 
+  rc= mysql_stmt_execute(stmt);
+  check_execute(stmt, rc);
+
   rc= mysql_stmt_bind_result(stmt, my_bind);
   check_execute(stmt, rc);
 
@@ -9901,7 +9904,7 @@
   MYSQL_STMT *stmt;
   const char *query= "rename table t1 to t2, t3 to t4";
   int rc;
-  myheader("test_table_manipulation");
+  myheader("test_table_rename");
 
   rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, t2, t3, t4");
   myquery(rc);
@@ -11132,7 +11135,7 @@
   int rc;
   MYSQL_STMT backup;
 
-  myheader("test_bug4296");
+  myheader("test_bug4236");
 
   stmt= mysql_stmt_init(mysql);
 
@@ -17388,6 +17391,214 @@
 }
 
 
+static void test_wl4166_1()
+{
+  MYSQL_STMT *stmt;
+  int        int_data;
+  char       str_data[50];
+  char       tiny_data;
+  short      small_data;
+  longlong   big_data;
+  float      real_data;
+  double     double_data;
+  ulong      length[7];
+  my_bool    is_null[7];
+  MYSQL_BIND my_bind[7];
+  int rc;
+  int i;
+
+  myheader("test_wl4166_1");
+
+  rc= mysql_query(mysql, "DROP TABLE IF EXISTS table_4166");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "CREATE TABLE table_4166(col1 tinyint NOT NULL, "
+                         "col2 varchar(15), col3 int, "
+                         "col4 smallint, col5 bigint, "
+                         "col6 float, col7 double, "
+                         "colX varchar(10) default NULL)");
+  myquery(rc);
+
+  stmt= mysql_simple_prepare(mysql,
+    "INSERT INTO table_4166(col1, col2, col3, col4, col5, col6, col7) "
+    "VALUES(?, ?, ?, ?, ?, ?, ?)");
+  check_stmt(stmt);
+
+  verify_param_count(stmt, 7);
+
+  bzero(my_bind, sizeof(my_bind));
+  /* tinyint */
+  my_bind[0].buffer_type= MYSQL_TYPE_TINY;
+  my_bind[0].buffer= (void *)&tiny_data;
+  /* string */
+  my_bind[1].buffer_type= MYSQL_TYPE_STRING;
+  my_bind[1].buffer= (void *)str_data;
+  my_bind[1].buffer_length= 1000;                  /* Max string length */
+  /* integer */
+  my_bind[2].buffer_type= MYSQL_TYPE_LONG;
+  my_bind[2].buffer= (void *)&int_data;
+  /* short */
+  my_bind[3].buffer_type= MYSQL_TYPE_SHORT;
+  my_bind[3].buffer= (void *)&small_data;
+  /* bigint */
+  my_bind[4].buffer_type= MYSQL_TYPE_LONGLONG;
+  my_bind[4].buffer= (void *)&big_data;
+  /* float */
+  my_bind[5].buffer_type= MYSQL_TYPE_FLOAT;
+  my_bind[5].buffer= (void *)&real_data;
+  /* double */
+  my_bind[6].buffer_type= MYSQL_TYPE_DOUBLE;
+  my_bind[6].buffer= (void *)&double_data;
+
+  for (i= 0; i < (int) array_elements(my_bind); i++)
+  {
+    my_bind[i].length= &length[i];
+    my_bind[i].is_null= &is_null[i];
+    is_null[i]= 0;
+  }
+
+  rc= mysql_stmt_bind_param(stmt, my_bind);
+  check_execute(stmt, rc);
+
+  int_data= 320;
+  small_data= 1867;
+  big_data= 1000;
+  real_data= 2;
+  double_data= 6578.001;
+
+  /* now, execute the prepared statement to insert 10 records.. */
+  for (tiny_data= 0; tiny_data < 10; tiny_data++)
+  {
+    length[1]= my_sprintf(str_data, (str_data, "MySQL%d", int_data));
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+    int_data += 25;
+    small_data += 10;
+    big_data += 100;
+    real_data += 1;
+    double_data += 10.09;
+  }
+
+  /* force a re-prepare with some DDL */
+
+  rc= mysql_query(mysql,
+    "ALTER TABLE table_4166 change colX colX varchar(20) default NULL");
+  myquery(rc);
+
+  /*
+    execute the prepared statement again,
+    without changing the types of parameters already bound.
+  */
+
+  for (tiny_data= 50; tiny_data < 60; tiny_data++)
+  {
+    length[1]= my_sprintf(str_data, (str_data, "MySQL%d", int_data));
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+    int_data += 25;
+    small_data += 10;
+    big_data += 100;
+    real_data += 1;
+    double_data += 10.09;
+  }
+
+  mysql_stmt_close(stmt);
+
+  rc= mysql_query(mysql, "DROP TABLE table_4166");
+  myquery(rc);
+}
+
+
+static void test_wl4166_2()
+{
+  MYSQL_STMT *stmt;
+  int        c_int;
+  MYSQL_TIME d_date;
+  MYSQL_BIND bind_out[2];
+  int rc;
+
+  myheader("test_wl4166_2");
+
+  rc= mysql_query(mysql, "drop table if exists t1");
+  myquery(rc);
+  rc= mysql_query(mysql, "create table t1 (c_int int, d_date date)");
+  myquery(rc);
+  rc= mysql_query(mysql,
+                  "insert into t1 (c_int, d_date) values (42, '1948-05-15')");
+  myquery(rc);
+
+  stmt= mysql_simple_prepare(mysql, "select * from t1");
+  check_stmt(stmt);
+
+  bzero(bind_out, sizeof(bind_out));
+  bind_out[0].buffer_type= MYSQL_TYPE_LONG;
+  bind_out[0].buffer= (void*) &c_int;
+
+  bind_out[1].buffer_type= MYSQL_TYPE_DATE;
+  bind_out[1].buffer= (void*) &d_date;
+
+  rc= mysql_stmt_bind_result(stmt, bind_out);
+  check_execute(stmt, rc);
+
+  /* int -> varchar transition */
+
+  rc= mysql_query(mysql,
+                  "alter table t1 change column c_int c_int varchar(11)");
+  myquery(rc);
+
+  rc= mysql_stmt_execute(stmt);
+  check_execute(stmt, rc);
+
+  rc= mysql_stmt_fetch(stmt);
+  check_execute(stmt, rc);
+
+  DIE_UNLESS(c_int == 42);
+  DIE_UNLESS(d_date.year == 1948);
+  DIE_UNLESS(d_date.month == 5);
+  DIE_UNLESS(d_date.day == 15);
+
+  rc= mysql_stmt_fetch(stmt);
+  DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+  /* varchar to int retrieval with truncation */
+
+  rc= mysql_query(mysql, "update t1 set c_int='abcde'");
+  myquery(rc);
+
+  rc= mysql_stmt_execute(stmt);
+  check_execute(stmt, rc);
+
+  rc= mysql_stmt_fetch(stmt);
+  check_execute_r(stmt, rc);
+
+  DIE_UNLESS(c_int == 0);
+
+  rc= mysql_stmt_fetch(stmt);
+  DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+  /* alter table and increase the number of columns */
+  rc= mysql_query(mysql, "alter table t1 add column d_int int");
+  myquery(rc);
+
+  rc= mysql_stmt_execute(stmt);
+  check_execute_r(stmt, rc);
+
+  rc= mysql_stmt_reset(stmt);
+  check_execute(stmt, rc);
+
+  /* decrease the number of columns */
+  rc= mysql_query(mysql, "alter table t1 drop d_date, drop d_int");
+  myquery(rc);
+
+  rc= mysql_stmt_execute(stmt);
+  check_execute_r(stmt, rc);
+
+  mysql_stmt_close(stmt);
+  rc= mysql_query(mysql, "drop table t1");
+  myquery(rc);
+
+}
+
 /**
   Bug#36004 mysql_stmt_prepare resets the list of warnings
 */
@@ -17738,6 +17949,8 @@
   { "test_bug31418", test_bug31418 },
   { "test_bug31669", test_bug31669 },
   { "test_bug28386", test_bug28386 },
+  { "test_wl4166_1", test_wl4166_1 },
+  { "test_wl4166_2", test_wl4166_2 },
   { "test_bug36004", test_bug36004 },
   { 0, 0 }
 };

Thread
push into mysql-6.0 branch (chad:2657 to 2658) Bug#36345Chad MILLER24 May