diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 22f6371b069..bde095b0aa8 100755 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -165,8 +165,7 @@ valgrind_flags="$valgrind_flags -DMYSQL_SERVER_SUFFIX=-valgrind-max" valgrind_configs="--with-valgrind" # # Used in -debug builds -debug_cflags="-DUNIV_MUST_NOT_INLINE -DEXTRA_DEBUG" -debug_cflags="$debug_cflags -DSAFE_MUTEX -DSAFEMALLOC" +debug_cflags="-DEXTRA_DEBUG -DSAFE_MUTEX -DSAFEMALLOC" error_inject="--with-error-inject " # # Base C++ flags for all builds diff --git a/CMakeLists.txt b/CMakeLists.txt index f73150bfc4a..a16679337ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,9 @@ INCLUDE(cpack_rpm) # Add macros INCLUDE(character_sets) +INCLUDE(cpu_info) INCLUDE(zlib) +INCLUDE(libevent) INCLUDE(ssl) INCLUDE(readline) INCLUDE(libutils) @@ -204,7 +206,7 @@ ENDFOREACH() # Add safemutex for debug configurations, except on Windows # (safemutex has never worked on Windows) -IF(NOT WIN32) +IF(NOT WIN32 AND NOT WITH_INNODB_MEMCACHED) FOREACH(LANG C CXX) SET(CMAKE_${LANG}_FLAGS_DEBUG "${CMAKE_${LANG}_FLAGS_DEBUG} -DSAFE_MUTEX") ENDFOREACH() @@ -256,10 +258,12 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) # Add bundled or system zlib. MYSQL_CHECK_ZLIB_WITH_COMPRESS() -# Optionally add bundled yassl/taocrypt or system openssl. +# Add bundled yassl/taocrypt or system openssl. MYSQL_CHECK_SSL() # Add readline or libedit. MYSQL_CHECK_READLINE() +# Add libevent +MYSQL_CHECK_LIBEVENT() # # Setup maintainer mode options. Platform checks are @@ -297,6 +301,7 @@ ADD_SUBDIRECTORY(strings) ADD_SUBDIRECTORY(vio) ADD_SUBDIRECTORY(regex) ADD_SUBDIRECTORY(mysys) +ADD_SUBDIRECTORY(mysys_ssl) ADD_SUBDIRECTORY(libmysql) ADD_SUBDIRECTORY(client) ADD_SUBDIRECTORY(extra) diff --git a/TODO b/TODO index 827d5a139ab..19c32379a1a 100644 --- a/TODO +++ b/TODO @@ -32,12 +32,10 @@ Short time TODO: - add support for host_error() - Enable performance_schema.host_cache in scripts/mysql_system_tables.sql -- Add full support for automatic timestamp. - (remove timestamp handling from ha_write()) - - Timour is working on this - - Add Sys_my_bind_addr(); Needed for perfschema +- Add support for format_section_buff in unireg.cc and table.cc + - mysql_socket_shutdown() was removed from vio/viosocket.cc. It was replaced with inline function in include/mysql/psi/mysql_socket.h but this doesn't call DisconnectEx(). We should check if we need to @@ -60,4 +58,3 @@ Sergei's notes: rpl_slave.cc XXX in mysql_client_test net_serv.cc - diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e4507f9c8ba..9fed5b4ea19 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -15,6 +15,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/mysys_ssl ${ZLIB_INCLUDE_DIR} ${SSL_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/libmysql @@ -25,6 +26,9 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) +## We will need libeay32.dll and ssleay32.dll when running client executables. +COPY_OPENSSL_DLLS(copy_openssl_client) + ADD_DEFINITIONS(${SSL_DEFINES}) MYSQL_ADD_EXECUTABLE(mysql completion_hash.cc mysql.cc readline.cc ${CMAKE_SOURCE_DIR}/sql/sql_string.cc) @@ -78,7 +82,7 @@ ENDIF(WIN32) ADD_EXECUTABLE(async_example async_example.c) TARGET_LINK_LIBRARIES(async_example mysqlclient) -SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin +SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin async_example PROPERTIES HAS_CXX TRUE) ADD_DEFINITIONS(-DHAVE_DLOPEN) diff --git a/client/mysql.cc b/client/mysql.cc index 3e9edd94ba6..90310f85e95 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -3504,9 +3504,9 @@ print_table_data(MYSQL_RES *result) { uint length= column_names ? field->name_length : 0; if (quick) - length=max(length,field->length); + length= MY_MAX(length,field->length); else - length=max(length,field->max_length); + length= MY_MAX(length,field->max_length); if (length < 4 && !IS_NOT_NULL(field->flags)) length=4; // Room for "NULL" field->max_length=length; @@ -3526,8 +3526,8 @@ print_table_data(MYSQL_RES *result) field->name, field->name + name_length); uint display_length= field->max_length + name_length - numcells; - tee_fprintf(PAGER, " %-*s |",(int) min(display_length, - MAX_COLUMN_LENGTH), + tee_fprintf(PAGER, " %-*s |",(int) MY_MIN(display_length, + MAX_COLUMN_LENGTH), field->name); num_flag[off]= IS_NUM(field->type); } @@ -3616,9 +3616,9 @@ static int get_field_disp_length(MYSQL_FIELD *field) uint length= column_names ? field->name_length : 0; if (quick) - length= max(length, field->length); + length= MY_MAX(length, field->length); else - length= max(length, field->max_length); + length= MY_MAX(length, field->max_length); if (length < 4 && !IS_NOT_NULL(field->flags)) length= 4; /* Room for "NULL" */ @@ -3634,6 +3634,7 @@ static int get_field_disp_length(MYSQL_FIELD *field) @returns The max number of characters in any row of this result */ + static int get_result_width(MYSQL_RES *result) { unsigned int len= 0; diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index c1f7d028c0d..8b3f0fdec79 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -585,7 +585,7 @@ static int extract_variable_from_show(DYNAMIC_STRING* ds, char* value) if ((value_end= strchr(value_start, '\n')) == NULL) return 1; /* Unexpected result */ - len= (size_t) min(FN_REFLEN, value_end-value_start); + len= (size_t) MY_MIN(FN_REFLEN, value_end-value_start); strncpy(value, value_start, len); value[len]= '\0'; return 0; diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 923539a181f..9a8901435c8 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -23,7 +23,8 @@ #include #include #include -#include /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ +#include +#include #define ADMIN_VERSION "9.1" #define MAX_MYSQL_VAR 512 diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index d15bcca4d0c..31a1d583c4f 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2345,7 +2345,7 @@ static Exit_status dump_local_log_entries(PRINT_EVENT_INFO *print_event_info, my_off_t length,tmp; for (length= start_position_mot ; length > 0 ; length-=tmp) { - tmp=min(length,sizeof(buff)); + tmp= MY_MIN(length,sizeof(buff)); if (my_b_read(file, buff, (uint) tmp)) { error("Failed reading from file."); diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index e96f855719e..e614d628630 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -784,8 +784,8 @@ static int handle_request_for_tables(char *tables, uint length) org= ptr= strmov(strmov(query, op), " TABLE "); ptr= fix_table_name(ptr, tables); - strmake(table_name_buff, org, min((int) sizeof(table_name_buff)-1, - (int) (ptr - org))); + strmake(table_name_buff, org, MY_MIN((int) sizeof(table_name_buff)-1, + (int) (ptr - org))); table_name= table_name_buff; ptr= strxmov(ptr, " ", options, NullS); query_length= (uint) (ptr - query); diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 2ec8414dbcf..87d6c80917a 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -6485,9 +6485,9 @@ int read_line(char *buf, int size) } else if ((c == '{' && (!my_strnncoll_simple(charset_info, (const uchar*) "while", 5, - (uchar*) buf, min(5, p - buf), 0) || + (uchar*) buf, MY_MIN(5, p - buf), 0) || !my_strnncoll_simple(charset_info, (const uchar*) "if", 2, - (uchar*) buf, min(2, p - buf), 0)))) + (uchar*) buf, MY_MIN(2, p - buf), 0)))) { /* Only if and while commands can be terminated by { */ *p++= c; diff --git a/cmake/configure.pl b/cmake/configure.pl index d5c0b9b061a..a71795a3bc8 100644 --- a/cmake/configure.pl +++ b/cmake/configure.pl @@ -150,6 +150,16 @@ foreach my $option (@ARGV) $cmakeargs = $cmakeargs." -DWITH_ZLIB=system"; next; } + if($option =~ /with-libevent=/) + { + $cmakeargs = $cmakeargs." -DWITH_LIBEVENT=system"; + next; + } + if($option =~ /with-libevent/) + { + $cmakeargs = $cmakeargs." -DWITH_LIBEVENT=bundled"; + next; + } if($option =~ /with-ssl=/) { $cmakeargs = $cmakeargs." -DWITH_SSL=yes"; @@ -237,6 +247,16 @@ foreach my $option (@ARGV) print("configure.pl : ignoring $option\n"); next; } + if ($option =~ /with-client-ldflags/) + { + print("configure.pl : ignoring $option\n"); + next; + } + if ($option =~ /with-mysqld-ldflags=/) + { + print("configure.pl : ignoring $option\n"); + next; + } $option = uc($option); $option =~ s/-/_/g; diff --git a/cmake/cpu_info.cmake b/cmake/cpu_info.cmake new file mode 100644 index 00000000000..32b98142ace --- /dev/null +++ b/cmake/cpu_info.cmake @@ -0,0 +1,30 @@ +# Copyright (c) 2009, 2011, 2012 Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Symbols with information about the CPU. + +FIND_PROGRAM(GETCONF getconf) +MARK_AS_ADVANCED(GETCONF) + +IF(GETCONF) + EXECUTE_PROCESS( + COMMAND ${GETCONF} LEVEL1_DCACHE_LINESIZE + OUTPUT_VARIABLE CPU_LEVEL1_DCACHE_LINESIZE + ) +ENDIF() +IF(CPU_LEVEL1_DCACHE_LINESIZE AND CPU_LEVEL1_DCACHE_LINESIZE GREATER 0) +ELSE() + SET(CPU_LEVEL1_DCACHE_LINESIZE 64) +ENDIF() diff --git a/cmake/libevent.cmake b/cmake/libevent.cmake new file mode 100644 index 00000000000..54498e1bb15 --- /dev/null +++ b/cmake/libevent.cmake @@ -0,0 +1,89 @@ +# Copyright (C) 2011 Oracle and/or its affiliates. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +MACRO (MYSQL_USE_BUNDLED_LIBEVENT) + SET(LIBEVENT_LIBRARY event) + SET(LIBEVENT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libevent) + SET(LIBEVENT_FOUND TRUE) + SET(WITH_LIBEVENT "bundled" CACHE STRING "Use bundled libevent") + ADD_SUBDIRECTORY(libevent) + GET_TARGET_PROPERTY(src libevent SOURCES) + FOREACH(file ${src}) + SET(LIBEVENT_SOURCES ${LIBEVENT_SOURCES} ${CMAKE_SOURCE_DIR}/libevent/${file}) + ENDFOREACH() +ENDMACRO() + +# MYSQL_CHECK_LIBEVENT +# +# Provides the following configure options: +# WITH_LIBEVENT_BUNDLED +# If this is set,we use bindled libevent +# If this is not set,search for system libevent. +# if system libevent is not found, use bundled copy +# LIBEVENT_LIBRARIES, LIBEVENT_INCLUDE_DIR and LIBEVENT_SOURCES +# are set after this macro has run + +MACRO (MYSQL_CHECK_LIBEVENT) + + IF (NOT WITH_LIBEVENT) + SET(WITH_LIBEVENT "bundled" CACHE STRING "By default use bundled libevent on this platform") + ENDIF() + + IF(WITH_LIBEVENT STREQUAL "bundled") + MYSQL_USE_BUNDLED_LIBEVENT() + ELSEIF(WITH_LIBEVENT STREQUAL "system" OR WITH_LIBEVENT STREQUAL "yes") + SET(LIBEVENT_FIND_QUIETLY TRUE) + + IF (NOT LIBEVENT_INCLUDE_PATH) + set(LIBEVENT_INCLUDE_PATH /usr/local/include /opt/local/include) + ENDIF() + + find_path(LIBEVENT_INCLUDE_DIR event.h PATHS ${LIBEVENT_INCLUDE_PATH}) + + if (NOT LIBEVENT_INCLUDE_DIR) + MESSAGE(SEND_ERROR "Cannot find appropriate event.h in /usr/local/include or /opt/local/include. Use bundled libevent") + endif() + + IF (NOT LIBEVENT_LIB_PATHS) + set(LIBEVENT_LIB_PATHS /usr/local/lib /opt/local/lib) + ENDIF() + + find_library(LIBEVENT_LIB event PATHS ${LIBEVENT_LIB_PATHS}) + + if (NOT LIBEVENT_LIB) + MESSAGE(SEND_ERROR "Cannot find appropriate event lib in /usr/local/lib or /opt/local/lib. Use bundled libevent") + endif() + + IF (LIBEVENT_LIB AND LIBEVENT_INCLUDE_DIR) + set(LIBEVENT_FOUND TRUE) + set(LIBEVENT_LIBS ${LIBEVENT_LIB}) + ELSE() + set(LIBEVENT_FOUND FALSE) + ENDIF() + + IF(LIBEVENT_FOUND) + SET(LIBEVENT_SOURCES "") + SET(LIBEVENT_LIBRARIES ${LIBEVENT_LIBS}) + SET(LIBEVENT_INCLUDE_DIRS ${LIBEVENT_INCLUDE_DIR}) + SET(LIBEVENT_DEFINES "-DHAVE_LIBEVENT") + ELSE() + IF(WITH_LIBEVENT STREQUAL "system") + MESSAGE(SEND_ERROR "Cannot find appropriate system libraries for libevent. Use bundled libevent") + ENDIF() + MYSQL_USE_BUNDLED_LIBEVENT() + ENDIF() + + ENDIF() +ENDMACRO() diff --git a/cmake/libutils.cmake b/cmake/libutils.cmake index 7c13df05ca4..2da701d39b0 100644 --- a/cmake/libutils.cmake +++ b/cmake/libutils.cmake @@ -304,20 +304,22 @@ FUNCTION(GET_DEPENDEND_OS_LIBS target result) SET(${result} ${ret} PARENT_SCOPE) ENDFUNCTION() -MACRO(RESTRICT_SYMBOL_EXPORTS target) - SET(VISIBILITY_HIDDEN_FLAG) +SET(VISIBILITY_HIDDEN_FLAG) - IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) - CHECK_C_COMPILER_FLAG("-fvisibility=hidden" HAVE_VISIBILITY_HIDDEN) - IF(HAVE_VISIBILITY_HIDDEN) - SET(VISIBILITY_HIDDEN_FLAG "-fvisibility=hidden") - ENDIF() +IF(CMAKE_COMPILER_IS_GNUCXX AND UNIX) + CHECK_C_COMPILER_FLAG("-fvisibility=hidden" HAVE_VISIBILITY_HIDDEN) + IF(HAVE_VISIBILITY_HIDDEN) + SET(VISIBILITY_HIDDEN_FLAG "-fvisibility=hidden") ENDIF() +ENDIF() - IF(CMAKE_C_COMPILER_ID MATCHES "SunPro") - SET(VISIBILITY_HIDDEN_FLAG "-xldscope=hidden") - ENDIF() +IF(CMAKE_C_COMPILER_ID MATCHES "SunPro") + SET(VISIBILITY_HIDDEN_FLAG "-xldscope=hidden") +ENDIF() +# We try to hide the symbols in yassl/zlib to avoid name clashes with +# other libraries like openssl. +FUNCTION(RESTRICT_SYMBOL_EXPORTS target) IF(VISIBILITY_HIDDEN_FLAG) GET_TARGET_PROPERTY(COMPILE_FLAGS ${target} COMPILE_FLAGS) IF(NOT COMPILE_FLAGS) @@ -327,5 +329,4 @@ MACRO(RESTRICT_SYMBOL_EXPORTS target) SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} ${VISIBILITY_HIDDEN_FLAG}") ENDIF() - -ENDMACRO() +ENDFUNCTION() diff --git a/cmake/os/Windows.cmake b/cmake/os/Windows.cmake index 42ddb12bf37..254d9f6d946 100644 --- a/cmake/os/Windows.cmake +++ b/cmake/os/Windows.cmake @@ -80,10 +80,6 @@ IF(MSVC) STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") ENDFOREACH() - # Remove support for exceptions - FOREACH(flag CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_INIT) - STRING(REPLACE "/EHsc" "" "${flag}" "${${flag}}") - ENDFOREACH() # Fix CMake's predefined huge stack size FOREACH(type EXE SHARED MODULE) diff --git a/cmake/ssl.cmake b/cmake/ssl.cmake index ca950229129..365494077dc 100644 --- a/cmake/ssl.cmake +++ b/cmake/ssl.cmake @@ -13,80 +13,228 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# We support different versions of SSL: +# - "bundled" uses source code in /extra/yassl +# - "system" (typically) uses headers/libraries in /usr/lib and /usr/lib64 +# - a custom installation of openssl can be used like this +# - cmake -DCMAKE_PREFIX_PATH= -DWITH_SSL="system" +# or +# - cmake -DWITH_SSL= +# +# The default value for WITH_SSL is "bundled" +# set in cmake/build_configurations/feature_set.cmake +# +# For custom build/install of openssl, see the accompanying README and +# INSTALL* files. When building with gcc, you must build the shared libraries +# (in addition to the static ones): +# ./config --prefix= --shared; make; make install +# On some platforms (mac) you need to choose 32/64 bit architecture. +# Build/Install of openssl on windows is slightly different: you need to run +# perl and nmake. You might also need to +# 'set path=\bin;%PATH% +# in order to find the .dll files at runtime. + +SET(WITH_SSL_DOC "bundled (use yassl)") +SET(WITH_SSL_DOC + "${WITH_SSL_DOC}, yes (prefer os library if present, otherwise use bundled)") +SET(WITH_SSL_DOC + "${WITH_SSL_DOC}, system (use os library)") +SET(WITH_SSL_DOC + "${WITH_SSL_DOC}, ") + MACRO (CHANGE_SSL_SETTINGS string) - SET(WITH_SSL ${string} CACHE STRING "Options are: no bundled yes(prefer os library if present otherwise use bundled) system(use os library)" FORCE) + SET(WITH_SSL ${string} CACHE STRING ${WITH_SSL_DOC} FORCE) ENDMACRO() MACRO (MYSQL_USE_BUNDLED_SSL) SET(INC_DIRS - ${CMAKE_SOURCE_DIR}/extra/yassl/include - ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include + ${CMAKE_SOURCE_DIR}/extra/yassl/include + ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/include ) SET(SSL_LIBRARIES yassl taocrypt) SET(SSL_INCLUDE_DIRS ${INC_DIRS}) SET(SSL_INTERNAL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/mySTL) - SET(SSL_DEFINES "-DHAVE_YASSL -DYASSL_PURE_C -DYASSL_PREFIX -DHAVE_OPENSSL -DMULTI_THREADED") + SET(SSL_DEFINES "-DHAVE_YASSL -DYASSL_PREFIX -DHAVE_OPENSSL -DMULTI_THREADED") CHANGE_SSL_SETTINGS("bundled") - #Remove -fno-implicit-templates - #(yassl sources cannot be compiled with it) - SET(SAVE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - IF(CMAKE_CXX_FLAGS) - STRING(REPLACE "-fno-implicit-templates" "" CMAKE_CXX_FLAGS - ${CMAKE_CXX_FLAGS}) - ENDIF() ADD_SUBDIRECTORY(extra/yassl) ADD_SUBDIRECTORY(extra/yassl/taocrypt) - SET(CMAKE_CXX_FLAGS ${SAVE_CXX_FLAGS}) GET_TARGET_PROPERTY(src yassl SOURCES) FOREACH(file ${src}) SET(SSL_SOURCES ${SSL_SOURCES} ${CMAKE_SOURCE_DIR}/extra/yassl/${file}) ENDFOREACH() GET_TARGET_PROPERTY(src taocrypt SOURCES) FOREACH(file ${src}) - SET(SSL_SOURCES ${SSL_SOURCES} ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/${file}) + SET(SSL_SOURCES ${SSL_SOURCES} + ${CMAKE_SOURCE_DIR}/extra/yassl/taocrypt/${file}) ENDFOREACH() ENDMACRO() # MYSQL_CHECK_SSL # # Provides the following configure options: -# WITH_SSL=[yes|no|bundled] +# WITH_SSL=[yes|bundled|system|] MACRO (MYSQL_CHECK_SSL) IF(NOT WITH_SSL) IF(WIN32) CHANGE_SSL_SETTINGS("bundled") ELSE() - CHANGE_SSL_SETTINGS("no") + SET(WITH_SSL "yes") ENDIF() ENDIF() + # See if WITH_SSL is of the form + FILE(GLOB WITH_SSL_HEADER ${WITH_SSL}/include/openssl/ssl.h) + IF (WITH_SSL_HEADER) + SET(WITH_SSL_PATH ${WITH_SSL} CACHE PATH "path to custom SSL installation") + ENDIF() + IF(WITH_SSL STREQUAL "bundled") MYSQL_USE_BUNDLED_SSL() - ELSEIF(WITH_SSL STREQUAL "system" OR WITH_SSL STREQUAL "yes") - # Check for system library - SET(OPENSSL_FIND_QUIETLY TRUE) - INCLUDE(FindOpenSSL) - FIND_LIBRARY(CRYPTO_LIBRARY crypto) - MARK_AS_ADVANCED(CRYPTO_LIBRARY) + # Reset some variables, in case we switch from /path/to/ssl to "bundled". + IF (WITH_SSL_PATH) + UNSET(WITH_SSL_PATH) + UNSET(WITH_SSL_PATH CACHE) + ENDIF() + IF (OPENSSL_ROOT_DIR) + UNSET(OPENSSL_ROOT_DIR) + UNSET(OPENSSL_ROOT_DIR CACHE) + ENDIF() + IF (OPENSSL_INCLUDE_DIR) + UNSET(OPENSSL_INCLUDE_DIR) + UNSET(OPENSSL_INCLUDE_DIR CACHE) + ENDIF() + IF (WIN32 AND OPENSSL_APPLINK_C) + UNSET(OPENSSL_APPLINK_C) + UNSET(OPENSSL_APPLINK_C CACHE) + ENDIF() + IF (OPENSSL_LIBRARIES) + UNSET(OPENSSL_LIBRARIES) + UNSET(OPENSSL_LIBRARIES CACHE) + ENDIF() + ELSEIF(WITH_SSL STREQUAL "system" OR + WITH_SSL STREQUAL "yes" OR + WITH_SSL_PATH + ) + # First search in WITH_SSL_PATH. + FIND_PATH(OPENSSL_ROOT_DIR + NAMES include/openssl/ssl.h + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + HINTS ${WITH_SSL_PATH} + ) + # Then search in standard places (if not found above). + FIND_PATH(OPENSSL_ROOT_DIR + NAMES include/openssl/ssl.h + ) + + FIND_PATH(OPENSSL_INCLUDE_DIR + NAMES openssl/ssl.h + HINTS ${OPENSSL_ROOT_DIR}/include + ) + + IF (WIN32) + FIND_FILE(OPENSSL_APPLINK_C + NAMES openssl/applink.c + HINTS ${OPENSSL_ROOT_DIR}/include + ) + MESSAGE(STATUS "OPENSSL_APPLINK_C ${OPENSSL_APPLINK_C}") + ENDIF() + + # On mac this list is <.dylib;.so;.a> + # We prefer static libraries, so we revert it here. + IF (WITH_SSL_PATH) + LIST(REVERSE CMAKE_FIND_LIBRARY_SUFFIXES) + ENDIF() + MESSAGE(STATUS "suffixes <${CMAKE_FIND_LIBRARY_SUFFIXES}>") + FIND_LIBRARY(OPENSSL_LIBRARIES + NAMES ssl ssleay32 ssleay32MD + HINTS ${OPENSSL_ROOT_DIR}/lib) + FIND_LIBRARY(CRYPTO_LIBRARY + NAMES crypto libeay32 + HINTS ${OPENSSL_ROOT_DIR}/lib) + IF (WITH_SSL_PATH) + LIST(REVERSE CMAKE_FIND_LIBRARY_SUFFIXES) + ENDIF() + + # Verify version number. Version information looks like: + # #define OPENSSL_VERSION_NUMBER 0x1000103fL + # Encoded as MNNFFPPS: major minor fix patch status + FILE(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" + OPENSSL_VERSION_NUMBER + REGEX "^#define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x[0-9].*" + ) + STRING(REGEX REPLACE + "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9]).*$" "\\1" + OPENSSL_MAJOR_VERSION "${OPENSSL_VERSION_NUMBER}" + ) + + IF(OPENSSL_INCLUDE_DIR AND + OPENSSL_LIBRARIES AND + CRYPTO_LIBRARY AND + OPENSSL_MAJOR_VERSION STREQUAL "1" + ) + SET(OPENSSL_FOUND TRUE) + ELSE() + SET(OPENSSL_FOUND FALSE) + ENDIF() + + MESSAGE(STATUS "OPENSSL_INCLUDE_DIR = ${OPENSSL_INCLUDE_DIR}") + MESSAGE(STATUS "OPENSSL_LIBRARIES = ${OPENSSL_LIBRARIES}") + MESSAGE(STATUS "CRYPTO_LIBRARY = ${CRYPTO_LIBRARY}") + MESSAGE(STATUS "OPENSSL_MAJOR_VERSION = ${OPENSSL_MAJOR_VERSION}") + INCLUDE(CheckSymbolExists) SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) CHECK_SYMBOL_EXISTS(SHA512_DIGEST_LENGTH "openssl/sha.h" HAVE_SHA512_DIGEST_LENGTH) - SET(CMAKE_REQUIRED_INCLUDES) - IF(OPENSSL_FOUND AND CRYPTO_LIBRARY AND HAVE_SHA512_DIGEST_LENGTH) + IF(OPENSSL_FOUND AND HAVE_SHA512_DIGEST_LENGTH) SET(SSL_SOURCES "") SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES} ${CRYPTO_LIBRARY}) + IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET(SSL_LIBRARIES ${SSL_LIBRARIES} ${LIBSOCKET}) + ENDIF() + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") + SET(SSL_LIBRARIES ${SSL_LIBRARIES} ${LIBDL}) + ENDIF() + MESSAGE(STATUS "SSL_LIBRARIES = ${SSL_LIBRARIES}") SET(SSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR}) SET(SSL_INTERNAL_INCLUDE_DIRS "") SET(SSL_DEFINES "-DHAVE_OPENSSL") - CHANGE_SSL_SETTINGS("system") ELSE() IF(WITH_SSL STREQUAL "system") MESSAGE(SEND_ERROR "Cannot find appropriate system libraries for SSL. Use WITH_SSL=bundled to enable SSL support") ENDIF() MYSQL_USE_BUNDLED_SSL() ENDIF() - ELSEIF(NOT WITH_SSL STREQUAL "no") - MESSAGE(SEND_ERROR "Wrong option for WITH_SSL. Valid values are : yes, no, bundled") + ELSE() + MESSAGE(SEND_ERROR + "Wrong option for WITH_SSL. Valid values are : "${WITH_SSL_DOC}) + ENDIF() +ENDMACRO() + + +# Many executables will depend on libeay32.dll and ssleay32.dll at runtime. +# In order to ensure we find the right version(s), we copy them into +# the same directory as the executables. +# NOTE: Using dlls will likely crash in malloc/free, +# see INSTALL.W32 which comes with the openssl sources. +# So we should be linking static versions of the libraries. +MACRO (COPY_OPENSSL_DLLS target_name) + IF (WIN32 AND WITH_SSL_PATH) + GET_FILENAME_COMPONENT(CRYPTO_NAME "${CRYPTO_LIBRARY}" NAME_WE) + GET_FILENAME_COMPONENT(OPENSSL_NAME "${OPENSSL_LIBRARIES}" NAME_WE) + FILE(GLOB HAVE_CRYPTO_DLL "${WITH_SSL_PATH}/bin/${CRYPTO_NAME}.dll") + FILE(GLOB HAVE_OPENSSL_DLL "${WITH_SSL_PATH}/bin/${OPENSSL_NAME}.dll") + IF (HAVE_CRYPTO_DLL AND HAVE_OPENSSL_DLL) + ADD_CUSTOM_COMMAND(OUTPUT ${target_name} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${WITH_SSL_PATH}/bin/${CRYPTO_NAME}.dll" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CRYPTO_NAME}.dll" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${WITH_SSL_PATH}/bin/${OPENSSL_NAME}.dll" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${OPENSSL_NAME}.dll" + ) + ADD_CUSTOM_TARGET(${target_name} ALL) + ENDIF() ENDIF() ENDMACRO() diff --git a/dbug/dbug.c b/dbug/dbug.c index b285b32fa17..9ec8044eaf1 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -1332,7 +1332,7 @@ void _db_dump_(uint _line_, const char *keyword, if (TRACING) { Indent(cs, cs->level + 1); - pos= min(max(cs->level-cs->stack->sub_level,0)*INDENT,80); + pos= MY_MIN(MY_MAX(cs->level-cs->stack->sub_level,0)*INDENT,80); } else { @@ -1737,7 +1737,7 @@ static void Indent(CODE_STATE *cs, int indent) { int count; - indent= max(indent-1-cs->stack->sub_level,0)*INDENT; + indent= MY_MAX(indent-1-cs->stack->sub_level,0)*INDENT; for (count= 0; count < indent ; count++) { if ((count % INDENT) == 0) diff --git a/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch b/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch index 4c84e14bf06..88ed0347cfb 100755 --- a/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch +++ b/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch @@ -20,9 +20,9 @@ DROP TABLE tmp_db; @@ -40,8 +38,6 @@ - REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','' FROM dual WHERE LOWER( @current_hostname) != 'localhost'; - REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'',''); - REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'',''); + REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','' FROM dual WHERE LOWER( @current_hostname) != 'localhost'; + REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','',''); + REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','',''); -INSERT INTO tmp_user (host,user) VALUES ('localhost',''); -INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost'; INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0; diff --git a/extra/comp_err.c b/extra/comp_err.c index fb51377ddc5..bf757122957 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -33,8 +33,9 @@ #include #include -#define MAX_ROWS 1000 +#define MAX_ROWS 2000 #define HEADER_LENGTH 32 /* Length of header in errmsg.sys */ +#define ERRMSG_VERSION 3 /* Version number of errmsg.sys */ #define DEFAULT_CHARSET_DIR "../sql/share/charsets" #define ER_PREFIX "ER_" #define ER_PREFIX2 "MARIA_ER_" @@ -50,9 +51,9 @@ static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace"; #endif /* Header for errmsg.sys files */ -uchar file_head[]= { 254, 254, 2, 2 }; +uchar file_head[]= { 254, 254, 2, ERRMSG_VERSION }; /* Store positions to each error message row to store in errmsg.sys header */ -uint file_pos[MAX_ROWS]; +uint file_pos[MAX_ROWS+1]; const char *empty_string= ""; /* For empty states */ /* @@ -379,9 +380,11 @@ static int create_sys_files(struct languages *lang_head, if (my_fwrite(to, (uchar*) head, HEADER_LENGTH, MYF(MY_WME | MY_FNABP))) goto err; + file_pos[row_count]= (ftell(to) - start_pos); for (i= 0; i < row_count; i++) { - int2store(head, file_pos[i]); + /* Store length of each string */ + int2store(head, file_pos[i+1] - file_pos[i]); if (my_fwrite(to, (uchar*) head, 2, MYF(MY_WME | MY_FNABP))) goto err; } diff --git a/extra/my_print_defaults.c b/extra/my_print_defaults.c index 7558d6d00ae..e91163dde1c 100644 --- a/extra/my_print_defaults.c +++ b/extra/my_print_defaults.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #define load_default_groups mysqld_groups @@ -33,6 +34,7 @@ #undef load_default_groups my_bool opt_mysqld; + const char *config_file="my"; /* Default config file */ uint verbose= 0, opt_defaults_file_used= 0; const char *default_dbug_option="d:t:o,/tmp/my_print_defaults.trace"; diff --git a/include/big_endian.h b/include/big_endian.h new file mode 100644 index 00000000000..021b6abc383 --- /dev/null +++ b/include/big_endian.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +/* + Data in big-endian format. +*/ +#define float4store(T,A) do { *(T)= ((uchar *) &A)[3];\ + *((T)+1)=(char) ((uchar *) &A)[2];\ + *((T)+2)=(char) ((uchar *) &A)[1];\ + *((T)+3)=(char) ((uchar *) &A)[0]; } while(0) + +#define float4get(V,M) do { float def_temp;\ + ((uchar*) &def_temp)[0]=(M)[3];\ + ((uchar*) &def_temp)[1]=(M)[2];\ + ((uchar*) &def_temp)[2]=(M)[1];\ + ((uchar*) &def_temp)[3]=(M)[0];\ + (V)=def_temp; } while(0) + +#define float8store(T,V) do { *(T)= ((uchar *) &V)[7];\ + *((T)+1)=(char) ((uchar *) &V)[6];\ + *((T)+2)=(char) ((uchar *) &V)[5];\ + *((T)+3)=(char) ((uchar *) &V)[4];\ + *((T)+4)=(char) ((uchar *) &V)[3];\ + *((T)+5)=(char) ((uchar *) &V)[2];\ + *((T)+6)=(char) ((uchar *) &V)[1];\ + *((T)+7)=(char) ((uchar *) &V)[0]; } while(0) + +#define float8get(V,M) do { double def_temp;\ + ((uchar*) &def_temp)[0]=(M)[7];\ + ((uchar*) &def_temp)[1]=(M)[6];\ + ((uchar*) &def_temp)[2]=(M)[5];\ + ((uchar*) &def_temp)[3]=(M)[4];\ + ((uchar*) &def_temp)[4]=(M)[3];\ + ((uchar*) &def_temp)[5]=(M)[2];\ + ((uchar*) &def_temp)[6]=(M)[1];\ + ((uchar*) &def_temp)[7]=(M)[0];\ + (V) = def_temp; } while(0) + +#define ushortget(V,M) do { V = (uint16) (((uint16) ((uchar) (M)[1]))+\ + ((uint16) ((uint16) (M)[0]) << 8)); } while(0) +#define shortget(V,M) do { V = (short) (((short) ((uchar) (M)[1]))+\ + ((short) ((short) (M)[0]) << 8)); } while(0) +#define longget(V,M) do { int32 def_temp;\ + ((uchar*) &def_temp)[0]=(M)[0];\ + ((uchar*) &def_temp)[1]=(M)[1];\ + ((uchar*) &def_temp)[2]=(M)[2];\ + ((uchar*) &def_temp)[3]=(M)[3];\ + (V)=def_temp; } while(0) +#define ulongget(V,M) do { uint32 def_temp;\ + ((uchar*) &def_temp)[0]=(M)[0];\ + ((uchar*) &def_temp)[1]=(M)[1];\ + ((uchar*) &def_temp)[2]=(M)[2];\ + ((uchar*) &def_temp)[3]=(M)[3];\ + (V)=def_temp; } while(0) +#define shortstore(T,A) do { uint def_temp=(uint) (A) ;\ + *(((char*)T)+1)=(char)(def_temp); \ + *(((char*)T)+0)=(char)(def_temp >> 8); } while(0) +#define longstore(T,A) do { *(((char*)T)+3)=((A));\ + *(((char*)T)+2)=(((A) >> 8));\ + *(((char*)T)+1)=(((A) >> 16));\ + *(((char*)T)+0)=(((A) >> 24)); } while(0) + +#define floatget(V,M) memcpy(&V, (M), sizeof(float)) +/* Cast away type qualifiers (necessary as macro takes argument by value). */ +#define floatstore(T,V) memcpy((T), (void*) (&V), sizeof(float)) +#define doubleget(V,M) memcpy(&V, (M), sizeof(double)) +/* Cast away type qualifiers (necessary as macro takes argument by value). */ +#define doublestore(T,V) memcpy((T), (void*) &V, sizeof(double)) +#define longlongget(V,M) memcpy(&V, (M), sizeof(ulonglong)) +#define longlongstore(T,V) memcpy((T), &V, sizeof(ulonglong)) diff --git a/include/byte_order_generic.h b/include/byte_order_generic.h new file mode 100644 index 00000000000..d4ac27eeb9c --- /dev/null +++ b/include/byte_order_generic.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +/* + Endianness-independent definitions for architectures other + than the x86 architecture. +*/ +#define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\ + ((int16) ((int16) (A)[1]) << 8)) +#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ + (((uint32) 255L << 24) | \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0])) : \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0]))) +#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\ + (((int32) ((uchar) (A)[1]) << 8)) +\ + (((int32) ((uchar) (A)[2]) << 16)) +\ + (((int32) ((int16) (A)[3]) << 24))) +#define sint8korr(A) (longlong) uint8korr(A) +#define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\ + ((uint16) ((uchar) (A)[1]) << 8)) +#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16)) +#define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint6korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) + \ + (((uint32) ((uchar) (A)[1])) << 8) + \ + (((uint32) ((uchar) (A)[2])) << 16) + \ + (((uint32) ((uchar) (A)[3])) << 24)) + \ + (((ulonglong) ((uchar) (A)[4])) << 32) + \ + (((ulonglong) ((uchar) (A)[5])) << 40)) +#define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) (((uint32) ((uchar) (A)[4])) +\ + (((uint32) ((uchar) (A)[5])) << 8) +\ + (((uint32) ((uchar) (A)[6])) << 16) +\ + (((uint32) ((uchar) (A)[7])) << 24))) <<\ + 32)) +#define int2store(T,A) do { uint def_temp= (uint) (A) ;\ + *((uchar*) (T))= (uchar)(def_temp); \ + *((uchar*) (T)+1)=(uchar)((def_temp >> 8)); \ + } while(0) +#define int3store(T,A) do { /*lint -save -e734 */\ + *((uchar*)(T))=(uchar) ((A));\ + *((uchar*) (T)+1)=(uchar) (((A) >> 8));\ + *((uchar*)(T)+2)=(uchar) (((A) >> 16)); \ + /*lint -restore */} while(0) +#define int4store(T,A) do { *((char *)(T))=(char) ((A));\ + *(((char *)(T))+1)=(char) (((A) >> 8));\ + *(((char *)(T))+2)=(char) (((A) >> 16));\ + *(((char *)(T))+3)=(char) (((A) >> 24));\ + } while(0) +#define int5store(T,A) do { *((char *)(T))= (char)((A)); \ + *(((char *)(T))+1)= (char)(((A) >> 8)); \ + *(((char *)(T))+2)= (char)(((A) >> 16)); \ + *(((char *)(T))+3)= (char)(((A) >> 24)); \ + *(((char *)(T))+4)= (char)(((A) >> 32)); \ + } while(0) +#define int6store(T,A) do { *((char *)(T))= (char)((A)); \ + *(((char *)(T))+1)= (char)(((A) >> 8)); \ + *(((char *)(T))+2)= (char)(((A) >> 16)); \ + *(((char *)(T))+3)= (char)(((A) >> 24)); \ + *(((char *)(T))+4)= (char)(((A) >> 32)); \ + *(((char *)(T))+5)= (char)(((A) >> 40)); \ + } while(0) +#define int8store(T,A) do { uint def_temp= (uint) (A), \ + def_temp2= (uint) ((A) >> 32); \ + int4store((T),def_temp); \ + int4store((T+4),def_temp2);\ + } while(0) diff --git a/include/byte_order_generic_x86.h b/include/byte_order_generic_x86.h new file mode 100644 index 00000000000..0a71a17829b --- /dev/null +++ b/include/byte_order_generic_x86.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +/* + Optimized function-like macros for the x86 architecture (_WIN32 included). +*/ +#define sint2korr(A) (*((const int16 *) (A))) +#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ + (((uint32) 255L << 24) | \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0])) : \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0]))) +#define sint4korr(A) (*((const long *) (A))) +#define uint2korr(A) (*((const uint16 *) (A))) + +/* + Attention: Please, note, uint3korr reads 4 bytes (not 3)! + It means, that you have to provide enough allocated space. +*/ +#if defined(HAVE_valgrind) && !defined(_WIN32) +#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16)) +#else +#define uint3korr(A) (long) (*((const unsigned int *) (A)) & 0xFFFFFF) +#endif + +#define uint4korr(A) (*((const uint32 *) (A))) +#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint6korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) + \ + (((uint32) ((uchar) (A)[1])) << 8) + \ + (((uint32) ((uchar) (A)[2])) << 16) + \ + (((uint32) ((uchar) (A)[3])) << 24)) + \ + (((ulonglong) ((uchar) (A)[4])) << 32) + \ + (((ulonglong) ((uchar) (A)[5])) << 40)) +#define uint8korr(A) (*((const ulonglong *) (A))) +#define sint8korr(A) (*((const longlong *) (A))) + +#define int2store(T,A) *((uint16*) (T))= (uint16) (A) +#define int3store(T,A) do { *(T)= (uchar) ((A));\ + *(T+1)=(uchar) (((uint) (A) >> 8));\ + *(T+2)=(uchar) (((A) >> 16));\ + } while (0) +#define int4store(T,A) *((long *) (T))= (long) (A) +#define int5store(T,A) do { *(T)= (uchar)((A));\ + *((T)+1)=(uchar) (((A) >> 8));\ + *((T)+2)=(uchar) (((A) >> 16));\ + *((T)+3)=(uchar) (((A) >> 24));\ + *((T)+4)=(uchar) (((A) >> 32));\ + } while(0) +#define int6store(T,A) do { *(T)= (uchar)((A)); \ + *((T)+1)=(uchar) (((A) >> 8)); \ + *((T)+2)=(uchar) (((A) >> 16)); \ + *((T)+3)=(uchar) (((A) >> 24)); \ + *((T)+4)=(uchar) (((A) >> 32)); \ + *((T)+5)=(uchar) (((A) >> 40)); \ + } while(0) +#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A) +typedef union { + double v; + long m[2]; +} doubleget_union; +#define doubleget(V,M) \ +do { doubleget_union _tmp; \ + _tmp.m[0] = *((const long*)(M)); \ + _tmp.m[1] = *(((const long*) (M))+1); \ + (V) = _tmp.v; } while(0) +#define doublestore(T,V) \ +do { *((long *) T) = ((const doubleget_union *)&V)->m[0]; \ + *(((long *) T)+1) = ((const doubleget_union *)&V)->m[1]; \ + } while (0) +#define float4get(V,M) \ +do { *((float *) &(V)) = *((const float*) (M)); } while(0) +#define float8get(V,M) doubleget((V),(M)) +#define float4store(V,M) memcpy((uchar*)(V), (uchar*)(&M), sizeof(float)) +#define floatstore(T,V) memcpy((uchar*)(T), (uchar*)(&V), sizeof(float)) +#define floatget(V,M) memcpy((uchar*)(&V),(uchar*) (M), sizeof(float)) +#define float8store(V,M) doublestore((V),(M)) diff --git a/include/byte_order_generic_x86_64.h b/include/byte_order_generic_x86_64.h new file mode 100644 index 00000000000..877c1574dfa --- /dev/null +++ b/include/byte_order_generic_x86_64.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +/* + Optimized function-like macros for the x86 architecture (_WIN32 included). +*/ +#define sint2korr(A) (int16) (*((int16 *) (A))) +#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ + (((uint32) 255L << 24) | \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0])) : \ + (((uint32) (uchar) (A)[2]) << 16) |\ + (((uint32) (uchar) (A)[1]) << 8) | \ + ((uint32) (uchar) (A)[0]))) +#define sint4korr(A) (int32) (*((int32 *) (A))) +#define uint2korr(A) (uint16) (*((uint16 *) (A))) +/* + Attention: Please, note, uint3korr reads 4 bytes (not 3)! + It means, that you have to provide enough allocated space. +*/ +#if defined(HAVE_purify) && !defined(_WIN32) +#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16)) +#else +#define uint3korr(A) (uint32) (*((unsigned int *) (A)) & 0xFFFFFF) +#endif +#define uint4korr(A) (uint32) (*((uint32 *) (A))) +#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ + (((uint32) ((uchar) (A)[1])) << 8) +\ + (((uint32) ((uchar) (A)[2])) << 16) +\ + (((uint32) ((uchar) (A)[3])) << 24)) +\ + (((ulonglong) ((uchar) (A)[4])) << 32)) +#define uint6korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) + \ + (((uint32) ((uchar) (A)[1])) << 8) + \ + (((uint32) ((uchar) (A)[2])) << 16) + \ + (((uint32) ((uchar) (A)[3])) << 24)) + \ + (((ulonglong) ((uchar) (A)[4])) << 32) + \ + (((ulonglong) ((uchar) (A)[5])) << 40)) +#define uint8korr(A) (ulonglong) (*((ulonglong *) (A))) +#define sint8korr(A) (longlong) (*((longlong *) (A))) + +#define int2store(T,A) do { uchar *pT= (uchar*)(T);\ + *((uint16*)(pT))= (uint16) (A);\ + } while (0) + +#define int3store(T,A) do { *(T)= (uchar) ((A));\ + *(T+1)=(uchar) (((uint) (A) >> 8));\ + *(T+2)=(uchar) (((A) >> 16));\ + } while (0) +#define int4store(T,A) do { uchar *pT= (uchar*)(T);\ + *((uint32 *) (pT))= (uint32) (A); \ + } while (0) + +#define int5store(T,A) do { *(T)= (uchar)((A));\ + *((T)+1)=(uchar) (((A) >> 8));\ + *((T)+2)=(uchar) (((A) >> 16));\ + *((T)+3)=(uchar) (((A) >> 24));\ + *((T)+4)=(uchar) (((A) >> 32));\ + } while(0) +#define int6store(T,A) do { *(T)= (uchar)((A)); \ + *((T)+1)=(uchar) (((A) >> 8)); \ + *((T)+2)=(uchar) (((A) >> 16)); \ + *((T)+3)=(uchar) (((A) >> 24)); \ + *((T)+4)=(uchar) (((A) >> 32)); \ + *((T)+5)=(uchar) (((A) >> 40)); \ + } while(0) +#define int8store(T,A) do { uchar *pT= (uchar*)(T);\ + *((ulonglong *) (pT))= (ulonglong) (A);\ + } while(0) diff --git a/include/crypt_genhash_impl.h b/include/crypt_genhash_impl.h new file mode 100644 index 00000000000..af5afd23e86 --- /dev/null +++ b/include/crypt_genhash_impl.h @@ -0,0 +1,32 @@ +/* defines and prototypes for using crypt_genhash_impl.cc */ + +#ifndef CRYPT_HASHGEN_IMPL_H +#define CRYPT_HASHGEN_IMPL_H +#define ROUNDS_DEFAULT 5000 +#define ROUNDS_MIN 1000 +#define ROUNDS_MAX 999999999 +#define MIXCHARS 32 +#define CRYPT_SALT_LENGTH 20 +#define CRYPT_MAGIC_LENGTH 3 +#define CRYPT_PARAM_LENGTH 13 +#define SHA256_HASH_LENGTH 43 +#define CRYPT_MAX_PASSWORD_SIZE (CRYPT_SALT_LENGTH + \ + SHA256_HASH_LENGTH + \ + CRYPT_MAGIC_LENGTH + \ + CRYPT_PARAM_LENGTH) + +int extract_user_salt(char **salt_begin, + char **salt_end); +C_MODE_START +char * +my_crypt_genhash(char *ctbuffer, + size_t ctbufflen, + const char *plaintext, + int plaintext_len, + const char *switchsalt, + const char **params); +void generate_user_salt(char *buffer, int buffer_len); +void xor_string(char *to, int to_len, char *pattern, int pattern_len); + +C_MODE_END +#endif diff --git a/include/errmsg.h b/include/errmsg.h index 64ec2df395c..b839060a881 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -16,8 +16,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -/* Error messages for MySQL clients */ -/* (Error messages for the daemon are in sql/share/errmsg.txt) */ +/* + Error messages numbers for MySQL clients. + The error messages itself are in libmysql/errmsg.c + + Error messages for the mysqld daemon are in sql/share/errmsg.txt +*/ #ifdef __cplusplus extern "C" { @@ -102,7 +106,9 @@ extern const char *client_errors[]; /* Error messages */ #define CR_NEW_STMT_METADATA 2057 #define CR_ALREADY_CONNECTED 2058 #define CR_AUTH_PLUGIN_CANNOT_LOAD 2059 -#define CR_ERROR_LAST /*Copy last error nr:*/ 2059 +#define CR_DUPLICATE_CONNECTION_ATTR 2060 +#define CR_AUTH_PLUGIN_ERR 2061 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2061 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */ #endif /* ERRMSG_INCLUDED */ diff --git a/include/ft_global.h b/include/ft_global.h index 73726018d0a..aad3b4cb56e 100644 --- a/include/ft_global.h +++ b/include/ft_global.h @@ -43,11 +43,32 @@ struct _ft_vft void (*reinit_search)(FT_INFO *); }; +typedef struct st_ft_info_ext FT_INFO_EXT; +struct _ft_vft_ext +{ + uint (*get_version)(); // Extended API version + ulonglong (*get_flags)(); + ulonglong (*get_docid)(FT_INFO_EXT *); + ulonglong (*count_matches)(FT_INFO_EXT *); +}; + +/* Flags for extended FT API */ +#define FTS_ORDERED_RESULT (1LL << 1) +#define FTS_DOCID_IN_RESULT (1LL << 2) + +#define FTS_DOC_ID_COL_NAME "FTS_DOC_ID" + #ifndef FT_CORE struct st_ft_info { struct _ft_vft *please; /* INTERCAL style :-) */ }; + +struct st_ft_info_ext +{ + struct _ft_vft *please; /* INTERCAL style :-) */ + struct _ft_vft_ext *could_you; +}; #endif extern const char *ft_stopword_file; diff --git a/include/little_endian.h b/include/little_endian.h new file mode 100644 index 00000000000..7223fea648f --- /dev/null +++ b/include/little_endian.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +/* + Data in little-endian format. +*/ + +#ifndef MY_BYTE_ORDER_ARCH_OPTIMIZED +#define float4get(V,M) memcpy(&V, (M), sizeof(float)) +#define float4store(V,M) memcpy(V, (&M), sizeof(float)) +#define float8get(V,M) doubleget((V),(M)) +#define float8store(V,M) doublestore((V),(M)) + +/* Bi-endian hardware.... */ +#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) +#define doublestore(T,V) do { *(((char*)T)+0)=(char) ((uchar *) &V)[4];\ + *(((char*)T)+1)=(char) ((uchar *) &V)[5];\ + *(((char*)T)+2)=(char) ((uchar *) &V)[6];\ + *(((char*)T)+3)=(char) ((uchar *) &V)[7];\ + *(((char*)T)+4)=(char) ((uchar *) &V)[0];\ + *(((char*)T)+5)=(char) ((uchar *) &V)[1];\ + *(((char*)T)+6)=(char) ((uchar *) &V)[2];\ + *(((char*)T)+7)=(char) ((uchar *) &V)[3]; }\ + while(0) +#define doubleget(V,M) do { double def_temp;\ + ((uchar*) &def_temp)[0]=(M)[4];\ + ((uchar*) &def_temp)[1]=(M)[5];\ + ((uchar*) &def_temp)[2]=(M)[6];\ + ((uchar*) &def_temp)[3]=(M)[7];\ + ((uchar*) &def_temp)[4]=(M)[0];\ + ((uchar*) &def_temp)[5]=(M)[1];\ + ((uchar*) &def_temp)[6]=(M)[2];\ + ((uchar*) &def_temp)[7]=(M)[3];\ + (V) = def_temp; } while(0) +#else /* Bi-endian hardware.... */ + +/* Cast away type qualifiers (necessary as macro takes argument by value). */ +#define doublestore(T,V) memcpy((T), (void*) &V, sizeof(double)) +#define doubleget(V,M) memcpy(&V, (M), sizeof(double)) + +#endif /* Bi-endian hardware.... */ + +#endif /* !MY_BYTE_ORDER_ARCH_OPTIMIZED */ + +#define ushortget(V,M) do { uchar *pM= (uchar*)(M);V = uint2korr(pM);} while(0) +#define shortget(V,M) do { uchar *pM= (uchar*)(M);V = sint2korr(pM);} while(0) +#define longget(V,M) do { uchar *pM= (uchar*)(M);V = sint4korr(pM);} while(0) +#define ulongget(V,M) do { uchar *pM= (uchar*)(M);V = uint4korr(pM);} while(0) +#define shortstore(T,V) int2store(T,V) +#define longstore(T,V) int4store(T,V) + +#ifndef floatstore +/* Cast away type qualifiers (necessary as macro takes argument by value). */ +#define floatstore(T,V) memcpy((T), (void*) (&V), sizeof(float)) +#define floatget(V,M) memcpy(&V, (M), sizeof(float)) +#endif +#ifndef doubleget +#define doubleget(V,M) memcpy(&V, (M), sizeof(double)) +#define doublestore(T,V) memcpy((T), (void *) &V, sizeof(double)) +#endif /* doubleget */ + +#define longlongget(V,M) memcpy(&V, (M), sizeof(ulonglong)) +#define longlongstore(T,V) memcpy((T), &V, sizeof(ulonglong)) diff --git a/include/m_ctype.h b/include/m_ctype.h index 7483e8f53a6..e3fb2dbc66e 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -137,6 +137,38 @@ extern MY_UNI_CTYPE my_uni_ctype[256]; #define MY_REPERTOIRE_EXTENDED 2 /* Extended characters: U+0080..U+FFFF */ #define MY_REPERTOIRE_UNICODE30 3 /* ASCII | EXTENDED: U+0000..U+FFFF */ +/* Flags for strxfrm */ +#define MY_STRXFRM_LEVEL1 0x00000001 /* for primary weights */ +#define MY_STRXFRM_LEVEL2 0x00000002 /* for secondary weights */ +#define MY_STRXFRM_LEVEL3 0x00000004 /* for tertiary weights */ +#define MY_STRXFRM_LEVEL4 0x00000008 /* fourth level weights */ +#define MY_STRXFRM_LEVEL5 0x00000010 /* fifth level weights */ +#define MY_STRXFRM_LEVEL6 0x00000020 /* sixth level weights */ +#define MY_STRXFRM_LEVEL_ALL 0x0000003F /* Bit OR for the above six */ +#define MY_STRXFRM_NLEVELS 6 /* Number of possible levels*/ + +#define MY_STRXFRM_PAD_WITH_SPACE 0x00000040 /* if pad result with spaces */ +#define MY_STRXFRM_PAD_TO_MAXLEN 0x00000080 /* if pad tail(for filesort) */ + +#define MY_STRXFRM_DESC_LEVEL1 0x00000100 /* if desc order for level1 */ +#define MY_STRXFRM_DESC_LEVEL2 0x00000200 /* if desc order for level2 */ +#define MY_STRXFRM_DESC_LEVEL3 0x00000300 /* if desc order for level3 */ +#define MY_STRXFRM_DESC_LEVEL4 0x00000800 /* if desc order for level4 */ +#define MY_STRXFRM_DESC_LEVEL5 0x00001000 /* if desc order for level5 */ +#define MY_STRXFRM_DESC_LEVEL6 0x00002000 /* if desc order for level6 */ +#define MY_STRXFRM_DESC_SHIFT 8 + +#define MY_STRXFRM_UNUSED_00004000 0x00004000 /* for future extensions */ +#define MY_STRXFRM_UNUSED_00008000 0x00008000 /* for future extensions */ + +#define MY_STRXFRM_REVERSE_LEVEL1 0x00010000 /* if reverse order for level1 */ +#define MY_STRXFRM_REVERSE_LEVEL2 0x00020000 /* if reverse order for level2 */ +#define MY_STRXFRM_REVERSE_LEVEL3 0x00040000 /* if reverse order for level3 */ +#define MY_STRXFRM_REVERSE_LEVEL4 0x00080000 /* if reverse order for level4 */ +#define MY_STRXFRM_REVERSE_LEVEL5 0x00100000 /* if reverse order for level5 */ +#define MY_STRXFRM_REVERSE_LEVEL6 0x00200000 /* if reverse order for level6 */ +#define MY_STRXFRM_REVERSE_SHIFT 16 + struct my_uni_idx_st { uint16 from; diff --git a/include/m_string.h b/include/m_string.h index 1f59fd06084..95b28d6d69a 100644 --- a/include/m_string.h +++ b/include/m_string.h @@ -168,7 +168,7 @@ size_t my_gcvt(double x, my_gcvt_arg_type type, int width, char *to, (DBL_DIG + 2) significant digits + sign + "." + ("e-NNN" or MAX_DECPT_FOR_F_FORMAT zeros for cases when |x|<1 and the 'f' format is used). */ -#define MY_GCVT_MAX_FIELD_WIDTH (DBL_DIG + 4 + max(5, MAX_DECPT_FOR_F_FORMAT)) \ +#define MY_GCVT_MAX_FIELD_WIDTH (DBL_DIG + 4 + MY_MAX(5, MAX_DECPT_FOR_F_FORMAT)) \ extern char *llstr(longlong value,char *buff); extern char *ullstr(longlong value,char *buff); diff --git a/include/my_base.h b/include/my_base.h index 0984ff8dfa9..c195830e35a 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -46,7 +46,8 @@ #define HA_OPEN_COPY 256 /* Open copy (for repair) */ /* Internal temp table, used for temporary results */ #define HA_OPEN_INTERNAL_TABLE 512 -#define HA_OPEN_MERGE_TABLE 1024 +#define HA_OPEN_NO_PSI_CALL 1024 /* Don't call/connect PSI */ +#define HA_OPEN_MERGE_TABLE 2048 /* The following is parameter to ha_rkey() how to use key */ @@ -194,6 +195,11 @@ enum ha_extra_function { HA_EXTRA_ATTACH_CHILDREN, HA_EXTRA_IS_ATTACHED_CHILDREN, HA_EXTRA_DETACH_CHILDREN, + /* + Prepare table for export + (e.g. quiesce the table and write table metadata). + */ + HA_EXTRA_EXPORT, HA_EXTRA_DETACH_CHILD, /* Inform handler we will force a close as part of flush */ HA_EXTRA_PREPARE_FOR_FORCED_CLOSE @@ -317,6 +323,23 @@ enum ha_base_keytype { #define HA_OPTION_RELIES_ON_SQL_LAYER 512 #define HA_OPTION_NULL_FIELDS 1024 #define HA_OPTION_PAGE_CHECKSUM 2048 +/* + STATS_PERSISTENT=1 has been specified in the SQL command (either CREATE + or ALTER TABLE). Table and index statistics that are collected by the + storage engine and used by the optimizer for query optimization will be + stored on disk and will not change after a server restart. +*/ +#define HA_OPTION_STATS_PERSISTENT 4096 +/* + STATS_PERSISTENT=0 has been specified in CREATE/ALTER TABLE. Statistics + for the table will be wiped away on server shutdown and new ones recalculated + after the server is started again. If none of HA_OPTION_STATS_PERSISTENT or + HA_OPTION_NO_STATS_PERSISTENT is set, this means that the setting is not + explicitly set at table level and the corresponding table will use whatever + is the global server default. +*/ +#define HA_OPTION_NO_STATS_PERSISTENT 8192 + /* .frm has extra create options in linked-list format */ #define HA_OPTION_TEXT_CREATE_OPTIONS_legacy (1L << 14) /* 5.2 to 5.5, unused since 10.0 */ #define HA_OPTION_TEMP_COMPRESS_RECORD (1L << 15) /* set by isamchk */ @@ -334,7 +357,7 @@ enum ha_base_keytype { #define HA_CREATE_PAGE_CHECKSUM 32 #define HA_CREATE_DELAY_KEY_WRITE 64 #define HA_CREATE_RELIES_ON_SQL_LAYER 128 - +#define HA_CREATE_INTERNAL_TABLE 256 /* Flags used by start_bulk_insert */ @@ -458,7 +481,8 @@ enum ha_base_keytype { /* It is not possible to log this statement */ #define HA_ERR_LOGGING_IMPOSSIBLE 170 /* The event was corrupt, leading to illegal data being read */ -#define HA_ERR_CORRUPT_EVENT 171 +#define HA_ERR_CORRUPT_EVENT 171 /* The event was corrupt, leading to + illegal data being read */ #define HA_ERR_NEW_FILE 172 /* New file format */ /* The event could not be processed no other handler error happened */ #define HA_ERR_ROWS_EVENT_APPLY 173 @@ -466,16 +490,19 @@ enum ha_base_keytype { #define HA_ERR_FILE_TOO_SHORT 175 /* File too short */ #define HA_ERR_WRONG_CRC 176 /* Wrong CRC on page */ #define HA_ERR_TOO_MANY_CONCURRENT_TRXS 177 /*Too many active concurrent transactions */ +/* There's no explicitly listed partition in table for the given value */ #define HA_ERR_NOT_IN_LOCK_PARTITIONS 178 #define HA_ERR_INDEX_COL_TOO_LONG 179 /* Index column length exceeds limit */ #define HA_ERR_INDEX_CORRUPT 180 /* Index corrupted */ #define HA_ERR_UNDO_REC_TOO_BIG 181 /* Undo log record too big */ -#define HA_ERR_TABLE_IN_FK_CHECK 182 /* Table being used in foreign key check */ -#define HA_FTS_INVALID_DOCID 183 /* Invalid InnoDB Doc ID */ -#define HA_ERR_ROW_NOT_VISIBLE 184 -#define HA_ERR_ABORTED_BY_USER 185 -#define HA_ERR_DISK_FULL 186 -#define HA_ERR_LAST 186 /* Copy of last error nr */ +#define HA_FTS_INVALID_DOCID 182 /* Invalid InnoDB Doc ID */ +#define HA_ERR_TABLE_IN_FK_CHECK 183 /* Table being used in foreign key check */ +#define HA_ERR_TABLESPACE_EXISTS 184 /* The tablespace existed in storage engine */ +#define HA_ERR_TOO_MANY_FIELDS 185 /* Table has too many columns */ +#define HA_ERR_ROW_NOT_VISIBLE 186 +#define HA_ERR_ABORTED_BY_USER 187 +#define HA_ERR_DISK_FULL 188 +#define HA_ERR_LAST 188 /* Copy of last error nr */ /* Number of different errors */ #define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) @@ -608,4 +635,17 @@ C_MODE_START typedef void (* invalidator_by_filename)(const char * filename); C_MODE_END + +enum durability_properties +{ + /* + Preserves the durability properties defined by the engine */ + HA_REGULAR_DURABILITY= 0, + /* + Ignore the durability properties defined by the engine and + write only in-memory entries. + */ + HA_IGNORE_DURABILITY= 1 +}; + #endif /* _my_base_h */ diff --git a/include/my_bitmap.h b/include/my_bitmap.h index 06f43f79df8..ef3274a8269 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -63,6 +63,7 @@ extern uint bitmap_set_next(MY_BITMAP *map); extern uint bitmap_get_first(const MY_BITMAP *map); extern uint bitmap_get_first_set(const MY_BITMAP *map); extern uint bitmap_bits_set(const MY_BITMAP *map); +extern uint bitmap_get_next_set(const MY_BITMAP *map, uint bitmap_bit); extern void bitmap_free(MY_BITMAP *map); extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit); extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size); diff --git a/include/my_byteorder.h b/include/my_byteorder.h new file mode 100644 index 00000000000..1f29248bfb2 --- /dev/null +++ b/include/my_byteorder.h @@ -0,0 +1,54 @@ +#ifndef MY_BYTEORDER_INCLUDED +#define MY_BYTEORDER_INCLUDED + +/* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +/* + Macro for reading 32-bit integer from network byte order (big-endian) + from an unaligned memory location. +*/ +#define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) | \ + (((uint32) ((uchar) (A)[2])) << 8) | \ + (((uint32) ((uchar) (A)[1])) << 16) | \ + (((uint32) ((uchar) (A)[0])) << 24)) + +/* + Function-like macros for reading and storing in machine independent + format (low byte first). There are 'korr' (assume 'corrector') variants + for integer types, but 'get' (assume 'getter') for floating point types. +*/ +#if defined(__i386__) || defined(_WIN32) +#define MY_BYTE_ORDER_ARCH_OPTIMIZED +#include "byte_order_generic_x86.h" +#elif defined(__x86_64__) +#include "byte_order_generic_x86_64.h" +#else +#include "byte_order_generic.h" +#endif + +/* + Function-like macros for reading and storing in machine format from/to + short/long to/from some place in memory V should be a variable (not on + a register) and M a pointer to byte. +*/ +#ifdef WORDS_BIGENDIAN +#include "big_endian.h" +#else +#include "little_endian.h" +#endif + +#endif /* MY_BYTEORDER_INCLUDED */ diff --git a/include/my_default.h b/include/my_default.h new file mode 100644 index 00000000000..1d556de69ee --- /dev/null +++ b/include/my_default.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2013 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Definitions for mysys/my_default.c */ + +#ifndef MY_DEFAULT_INCLUDED +#define MY_DEFAULT_INCLUDED + +C_MODE_START + +extern const char *my_defaults_extra_file; +extern const char *my_defaults_group_suffix; +extern const char *my_defaults_file; +extern my_bool my_getopt_use_args_separator; +extern my_bool my_getopt_is_args_separator(const char* arg); + +/* Define the type of function to be passed to process_default_option_files */ +typedef int (*Process_option_func)(void *ctx, const char *group_name, + const char *option); + +extern int get_defaults_options(int argc, char **argv, + char **defaults, char **extra_defaults, + char **group_suffix); +extern int my_load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv, const char ***); +extern int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv); +extern int my_search_option_files(const char *conf_file, int *argc, + char ***argv, uint *args_used, + Process_option_func func, void *func_ctx, + const char **default_directories); +extern void free_defaults(char **argv); +extern void my_print_default_files(const char *conf_file); +extern void print_defaults(const char *conf_file, const char **groups); + +C_MODE_END + +#endif /* MY_DEFAULT_INCLUDED */ diff --git a/include/my_getopt.h b/include/my_getopt.h index 589d9c9880c..2cbbca9cab9 100644 --- a/include/my_getopt.h +++ b/include/my_getopt.h @@ -17,7 +17,9 @@ #ifndef _my_getopt_h #define _my_getopt_h -#include "my_sys.h" /* loglevel */ +#include "my_sys.h" /* loglevel */ +/* my_getopt and my_default are almost always used together */ +#include C_MODE_START @@ -85,7 +87,6 @@ struct my_option void *app_type; /**< To be used by an application */ }; - typedef my_bool (*my_get_one_option)(int, const struct my_option *, char *); /** diff --git a/include/my_global.h b/include/my_global.h index 95b69c96dd7..78bf3cfd86c 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -50,11 +50,6 @@ #define _POSIX_THREAD_CPUTIME #endif /* __CYGWIN__ */ -/* to make command line shorter we'll define USE_PRAGMA_INTERFACE here */ -#ifdef USE_PRAGMA_IMPLEMENTATION -#define USE_PRAGMA_INTERFACE -#endif - #if defined(__OpenBSD__) && (OpenBSD >= 200411) #define HAVE_ERRNO_AS_DEFINE #endif @@ -130,6 +125,7 @@ /* Define missing access() modes. */ #define F_OK 0 #define W_OK 2 +#define R_OK 4 /* Test for read permission. */ /* Define missing file locking constants. */ #define F_RDLCK 1 @@ -348,6 +344,9 @@ C_MODE_END #ifdef HAVE_FCNTL_H #include #endif +#ifdef HAVE_SYS_TIMEB_H +#include /* Avoid warnings on SCO */ +#endif #ifdef HAVE_SYS_STAT_H #include #endif @@ -1046,296 +1045,7 @@ typedef ulong myf; /* Type of MyFlags in my_funcs */ #define MY_HOW_OFTEN_TO_ALARM 2 /* How often we want info on screen */ #define MY_HOW_OFTEN_TO_WRITE 10000 /* How often we want info on screen */ -/* - Define-funktions for reading and storing in machine independent format - (low byte first) -*/ - -/* Optimized store functions for Intel x86 */ -#if defined(__i386__) || defined(_WIN32) -#define sint2korr(A) (*((const int16 *) (A))) -#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ - (((uint32) 255L << 24) | \ - (((uint32) (uchar) (A)[2]) << 16) |\ - (((uint32) (uchar) (A)[1]) << 8) | \ - ((uint32) (uchar) (A)[0])) : \ - (((uint32) (uchar) (A)[2]) << 16) |\ - (((uint32) (uchar) (A)[1]) << 8) | \ - ((uint32) (uchar) (A)[0]))) -#define sint4korr(A) (*((const long *) (A))) -#define uint2korr(A) (*((const uint16 *) (A))) -#if defined(HAVE_valgrind) && !defined(_WIN32) -#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16)) -#else -/* - ATTENTION ! - - Please, note, uint3korr reads 4 bytes (not 3) ! - It means, that you have to provide enough allocated space ! -*/ -#define uint3korr(A) (long) (*((const unsigned int *) (A)) & 0xFFFFFF) -#endif /* HAVE_valgrind && !_WIN32 */ -#define uint4korr(A) (*((const uint32 *) (A))) -#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16) +\ - (((uint32) ((uchar) (A)[3])) << 24)) +\ - (((ulonglong) ((uchar) (A)[4])) << 32)) -#define uint6korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) + \ - (((uint32) ((uchar) (A)[1])) << 8) + \ - (((uint32) ((uchar) (A)[2])) << 16) + \ - (((uint32) ((uchar) (A)[3])) << 24)) + \ - (((ulonglong) ((uchar) (A)[4])) << 32) + \ - (((ulonglong) ((uchar) (A)[5])) << 40)) -#define uint8korr(A) (*((const ulonglong *) (A))) -#define sint8korr(A) (*((const longlong *) (A))) -#define int2store(T,A) *((uint16*) (T))= (uint16) (A) -#define int3store(T,A) do { *(T)= (uchar) ((A));\ - *(T+1)=(uchar) (((uint) (A) >> 8));\ - *(T+2)=(uchar) (((A) >> 16)); } while (0) -#define int4store(T,A) *((long *) (T))= (long) (A) -#define int5store(T,A) do { *(T)= (uchar)((A));\ - *((T)+1)=(uchar) (((A) >> 8));\ - *((T)+2)=(uchar) (((A) >> 16));\ - *((T)+3)=(uchar) (((A) >> 24)); \ - *((T)+4)=(uchar) (((A) >> 32)); } while(0) -#define int6store(T,A) do { *(T)= (uchar)((A)); \ - *((T)+1)=(uchar) (((A) >> 8)); \ - *((T)+2)=(uchar) (((A) >> 16)); \ - *((T)+3)=(uchar) (((A) >> 24)); \ - *((T)+4)=(uchar) (((A) >> 32)); \ - *((T)+5)=(uchar) (((A) >> 40)); } while(0) -#define int8store(T,A) *((ulonglong *) (T))= (ulonglong) (A) - -typedef union { - double v; - long m[2]; -} doubleget_union; -#define doubleget(V,M) \ -do { doubleget_union _tmp; \ - _tmp.m[0] = *((const long*)(M)); \ - _tmp.m[1] = *(((const long*) (M))+1); \ - (V) = _tmp.v; } while(0) -#define doublestore(T,V) do { *((long *) T) = ((const doubleget_union *)&V)->m[0]; \ - *(((long *) T)+1) = ((const doubleget_union *)&V)->m[1]; \ - } while (0) -#define float4get(V,M) do { *((float *) &(V)) = *((const float*) (M)); } while(0) -#define float8get(V,M) doubleget((V),(M)) -#define float4store(V,M) memcpy((uchar*) V,(uchar*) (&M),sizeof(float)) -#define floatstore(T,V) memcpy((uchar*)(T), (uchar*)(&V),sizeof(float)) -#define floatget(V,M) memcpy((uchar*) &V,(uchar*) (M),sizeof(float)) -#define float8store(V,M) doublestore((V),(M)) -#else - -/* - We're here if it's not a IA-32 architecture (Win32 and UNIX IA-32 defines - were done before) -*/ -#define sint2korr(A) (int16) (((int16) ((uchar) (A)[0])) +\ - ((int16) ((int16) (A)[1]) << 8)) -#define sint3korr(A) ((int32) ((((uchar) (A)[2]) & 128) ? \ - (((uint32) 255L << 24) | \ - (((uint32) (uchar) (A)[2]) << 16) |\ - (((uint32) (uchar) (A)[1]) << 8) | \ - ((uint32) (uchar) (A)[0])) : \ - (((uint32) (uchar) (A)[2]) << 16) |\ - (((uint32) (uchar) (A)[1]) << 8) | \ - ((uint32) (uchar) (A)[0]))) -#define sint4korr(A) (int32) (((int32) ((uchar) (A)[0])) +\ - (((int32) ((uchar) (A)[1]) << 8)) +\ - (((int32) ((uchar) (A)[2]) << 16)) +\ - (((int32) ((int16) (A)[3]) << 24))) -#define sint8korr(A) (longlong) uint8korr(A) -#define uint2korr(A) (uint16) (((uint16) ((uchar) (A)[0])) +\ - ((uint16) ((uchar) (A)[1]) << 8)) -#define uint3korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16)) -#define uint4korr(A) (uint32) (((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16) +\ - (((uint32) ((uchar) (A)[3])) << 24)) -#define uint5korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16) +\ - (((uint32) ((uchar) (A)[3])) << 24)) +\ - (((ulonglong) ((uchar) (A)[4])) << 32)) -#define uint6korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) + \ - (((uint32) ((uchar) (A)[1])) << 8) + \ - (((uint32) ((uchar) (A)[2])) << 16) + \ - (((uint32) ((uchar) (A)[3])) << 24)) + \ - (((ulonglong) ((uchar) (A)[4])) << 32) + \ - (((ulonglong) ((uchar) (A)[5])) << 40)) -#define uint8korr(A) ((ulonglong)(((uint32) ((uchar) (A)[0])) +\ - (((uint32) ((uchar) (A)[1])) << 8) +\ - (((uint32) ((uchar) (A)[2])) << 16) +\ - (((uint32) ((uchar) (A)[3])) << 24)) +\ - (((ulonglong) (((uint32) ((uchar) (A)[4])) +\ - (((uint32) ((uchar) (A)[5])) << 8) +\ - (((uint32) ((uchar) (A)[6])) << 16) +\ - (((uint32) ((uchar) (A)[7])) << 24))) <<\ - 32)) -#define int2store(T,A) do { uint def_temp= (uint) (A) ;\ - *((uchar*) (T))= (uchar)(def_temp); \ - *((uchar*) (T)+1)=(uchar)((def_temp >> 8)); \ - } while(0) -#define int3store(T,A) do { /*lint -save -e734 */\ - *((uchar*)(T))=(uchar) ((A));\ - *((uchar*) (T)+1)=(uchar) (((A) >> 8));\ - *((uchar*)(T)+2)=(uchar) (((A) >> 16)); \ - /*lint -restore */} while(0) -#define int4store(T,A) do { *((char *)(T))=(char) ((A));\ - *(((char *)(T))+1)=(char) (((A) >> 8));\ - *(((char *)(T))+2)=(char) (((A) >> 16));\ - *(((char *)(T))+3)=(char) (((A) >> 24)); } while(0) -#define int5store(T,A) do { *((char *)(T))= (char)((A)); \ - *(((char *)(T))+1)= (char)(((A) >> 8)); \ - *(((char *)(T))+2)= (char)(((A) >> 16)); \ - *(((char *)(T))+3)= (char)(((A) >> 24)); \ - *(((char *)(T))+4)= (char)(((A) >> 32)); \ - } while(0) -#define int6store(T,A) do { *((char *)(T))= (char)((A)); \ - *(((char *)(T))+1)= (char)(((A) >> 8)); \ - *(((char *)(T))+2)= (char)(((A) >> 16)); \ - *(((char *)(T))+3)= (char)(((A) >> 24)); \ - *(((char *)(T))+4)= (char)(((A) >> 32)); \ - *(((char *)(T))+5)= (char)(((A) >> 40)); \ - } while(0) -#define int8store(T,A) do { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \ - int4store((T),def_temp); \ - int4store((T+4),def_temp2); } while(0) -#ifdef WORDS_BIGENDIAN -#define float4store(T,A) do { *(T)= ((uchar *) &A)[3];\ - *((T)+1)=(char) ((uchar *) &A)[2];\ - *((T)+2)=(char) ((uchar *) &A)[1];\ - *((T)+3)=(char) ((uchar *) &A)[0]; } while(0) - -#define float4get(V,M) do { float def_temp;\ - ((uchar*) &def_temp)[0]=(M)[3];\ - ((uchar*) &def_temp)[1]=(M)[2];\ - ((uchar*) &def_temp)[2]=(M)[1];\ - ((uchar*) &def_temp)[3]=(M)[0];\ - (V)=def_temp; } while(0) -#define float8store(T,V) do { *(T)= ((uchar *) &V)[7];\ - *((T)+1)=(char) ((uchar *) &V)[6];\ - *((T)+2)=(char) ((uchar *) &V)[5];\ - *((T)+3)=(char) ((uchar *) &V)[4];\ - *((T)+4)=(char) ((uchar *) &V)[3];\ - *((T)+5)=(char) ((uchar *) &V)[2];\ - *((T)+6)=(char) ((uchar *) &V)[1];\ - *((T)+7)=(char) ((uchar *) &V)[0]; } while(0) - -#define float8get(V,M) do { double def_temp;\ - ((uchar*) &def_temp)[0]=(M)[7];\ - ((uchar*) &def_temp)[1]=(M)[6];\ - ((uchar*) &def_temp)[2]=(M)[5];\ - ((uchar*) &def_temp)[3]=(M)[4];\ - ((uchar*) &def_temp)[4]=(M)[3];\ - ((uchar*) &def_temp)[5]=(M)[2];\ - ((uchar*) &def_temp)[6]=(M)[1];\ - ((uchar*) &def_temp)[7]=(M)[0];\ - (V) = def_temp; } while(0) -#else -#define float4get(V,M) memcpy(&V, (M), sizeof(float)) -#define float4store(V,M) memcpy(V, (&M), sizeof(float)) - -#if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) -#define doublestore(T,V) do { *(((char*)T)+0)=(char) ((uchar *) &V)[4];\ - *(((char*)T)+1)=(char) ((uchar *) &V)[5];\ - *(((char*)T)+2)=(char) ((uchar *) &V)[6];\ - *(((char*)T)+3)=(char) ((uchar *) &V)[7];\ - *(((char*)T)+4)=(char) ((uchar *) &V)[0];\ - *(((char*)T)+5)=(char) ((uchar *) &V)[1];\ - *(((char*)T)+6)=(char) ((uchar *) &V)[2];\ - *(((char*)T)+7)=(char) ((uchar *) &V)[3]; }\ - while(0) -#define doubleget(V,M) do { double def_temp;\ - ((uchar*) &def_temp)[0]=(M)[4];\ - ((uchar*) &def_temp)[1]=(M)[5];\ - ((uchar*) &def_temp)[2]=(M)[6];\ - ((uchar*) &def_temp)[3]=(M)[7];\ - ((uchar*) &def_temp)[4]=(M)[0];\ - ((uchar*) &def_temp)[5]=(M)[1];\ - ((uchar*) &def_temp)[6]=(M)[2];\ - ((uchar*) &def_temp)[7]=(M)[3];\ - (V) = def_temp; } while(0) -#endif /* __FLOAT_WORD_ORDER */ - -#define float8get(V,M) doubleget((V),(M)) -#define float8store(V,M) doublestore((V),(M)) -#endif /* WORDS_BIGENDIAN */ - -#endif /* __i386__ OR _WIN32 */ - -/* - Macro for reading 32-bit integer from network byte order (big-endian) - from unaligned memory location. -*/ -#define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) |\ - (((uint32) ((uchar) (A)[2])) << 8) |\ - (((uint32) ((uchar) (A)[1])) << 16) |\ - (((uint32) ((uchar) (A)[0])) << 24)) -/* - Define-funktions for reading and storing in machine format from/to - short/long to/from some place in memory V should be a (not - register) variable, M is a pointer to byte -*/ - -#ifdef WORDS_BIGENDIAN - -#define ushortget(V,M) do { V = (uint16) (((uint16) ((uchar) (M)[1]))+\ - ((uint16) ((uint16) (M)[0]) << 8)); } while(0) -#define shortget(V,M) do { V = (short) (((short) ((uchar) (M)[1]))+\ - ((short) ((short) (M)[0]) << 8)); } while(0) -#define longget(V,M) do { int32 def_temp;\ - ((uchar*) &def_temp)[0]=(M)[0];\ - ((uchar*) &def_temp)[1]=(M)[1];\ - ((uchar*) &def_temp)[2]=(M)[2];\ - ((uchar*) &def_temp)[3]=(M)[3];\ - (V)=def_temp; } while(0) -#define ulongget(V,M) do { uint32 def_temp;\ - ((uchar*) &def_temp)[0]=(M)[0];\ - ((uchar*) &def_temp)[1]=(M)[1];\ - ((uchar*) &def_temp)[2]=(M)[2];\ - ((uchar*) &def_temp)[3]=(M)[3];\ - (V)=def_temp; } while(0) -#define shortstore(T,A) do { uint def_temp=(uint) (A) ;\ - *(((char*)T)+1)=(char)(def_temp); \ - *(((char*)T)+0)=(char)(def_temp >> 8); } while(0) -#define longstore(T,A) do { *(((char*)T)+3)=((A));\ - *(((char*)T)+2)=(((A) >> 8));\ - *(((char*)T)+1)=(((A) >> 16));\ - *(((char*)T)+0)=(((A) >> 24)); } while(0) - -#define floatget(V,M) memcpy(&V, (M), sizeof(float)) -#define floatstore(T,V) memcpy((T), (void*) (&V), sizeof(float)) -#define doubleget(V,M) memcpy(&V, (M), sizeof(double)) -#define doublestore(T,V) memcpy((T), (void *) &V, sizeof(double)) -#define longlongget(V,M) memcpy(&V, (M), sizeof(ulonglong)) -#define longlongstore(T,V) memcpy((T), &V, sizeof(ulonglong)) - -#else - -#define ushortget(V,M) do { V = uint2korr(M); } while(0) -#define shortget(V,M) do { V = sint2korr(M); } while(0) -#define longget(V,M) do { V = sint4korr(M); } while(0) -#define ulongget(V,M) do { V = uint4korr(M); } while(0) -#define shortstore(T,V) int2store(T,V) -#define longstore(T,V) int4store(T,V) -#ifndef floatstore -#define floatstore(T,V) memcpy((T), (void *) (&V), sizeof(float)) -#define floatget(V,M) memcpy(&V, (M), sizeof(float)) -#endif -#ifndef doubleget -#define doubleget(V,M) memcpy(&V, (M), sizeof(double)) -#define doublestore(T,V) memcpy((T), (void *) &V, sizeof(double)) -#endif /* doubleget */ -#define longlongget(V,M) memcpy(&V, (M), sizeof(ulonglong)) -#define longlongstore(T,V) memcpy((T), &V, sizeof(ulonglong)) - -#endif /* WORDS_BIGENDIAN */ +#include #ifdef HAVE_CHARSET_utf8 #define MYSQL_UNIVERSAL_CLIENT_CHARSET "utf8" @@ -1396,10 +1106,6 @@ static inline char *dlerror(void) #endif /* Define some useful general macros (should be done after all headers). */ -#if !defined(max) -#define max(a, b) ((a) > (b) ? (a) : (b)) -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif #define MY_MAX(a, b) ((a) > (b) ? (a) : (b)) #define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) diff --git a/include/my_handler_errors.h b/include/my_handler_errors.h index f2c51773e83..24b977c38ce 100644 --- a/include/my_handler_errors.h +++ b/include/my_handler_errors.h @@ -84,8 +84,10 @@ static const char *handler_error_messages[]= "Index column length exceeds limit", "Index corrupted", "Undo record too big", - "Table is being used in foreign key check", "Invalid InnoDB FTS Doc ID", + "Table is being used in foreign key check", + "Tablespace already exists", + "Too many columns", "Row is not visible by the current transaction", "Operation was interrupted by end user (probably kill command?)", "Disk full" diff --git a/include/my_md5.h b/include/my_md5.h index 1273616a19b..77557fb9346 100644 --- a/include/my_md5.h +++ b/include/my_md5.h @@ -1,8 +1,8 @@ #ifndef MY_MD5_INCLUDED #define MY_MD5_INCLUDED -/* Copyright (c) 2000, 2001, 2007 MySQL AB, 2009 Sun Microsystems, Inc. - Use is subject to license terms +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2013 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,79 +17,36 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/* See md5.c for explanation and copyright information. */ +#include "m_string.h" + +#define MD5_HASH_SIZE 16 /* Hash size in bytes */ /* - * $FreeBSD: src/contrib/cvs/lib/md5.h,v 1.2 1999/12/11 15:10:02 peter Exp $ - */ - -#if defined(HAVE_YASSL) || defined(HAVE_OPENSSL) -/* - Use MD5 implementation provided by the SSL libraries. + Wrapper function for MD5 implementation. */ - -#if defined(HAVE_YASSL) - -C_MODE_START - -void my_md5_hash(char *digest, const char *buf, int len); - -C_MODE_END - -#else /* HAVE_YASSL */ - -#include - -#define MY_MD5_HASH(digest, buf, len) \ -do { \ - MD5_CTX ctx; \ - MD5_Init (&ctx); \ - MD5_Update (&ctx, buf, len); \ - MD5_Final (digest, &ctx); \ -} while (0) - -#endif /* HAVE_YASSL */ - -#else /* HAVE_YASSL || HAVE_OPENSSL */ -/* Fallback to the MySQL's implementation. */ - -/* Unlike previous versions of this code, uint32 need not be exactly - 32 bits, merely 32 bits or more. Choosing a data type which is 32 - bits instead of 64 is not important; speed is considerably more - important. ANSI guarantees that "unsigned long" will be big enough, - and always using it seems to have few disadvantages. */ -typedef uint32 cvs_uint32; - -typedef struct { - cvs_uint32 buf[4]; - cvs_uint32 bits[2]; - unsigned char in[64]; -} my_MD5Context; - -C_MODE_START - -void my_MD5Init (my_MD5Context *context); -void my_MD5Update (my_MD5Context *context, - unsigned char const *buf, unsigned len); -void my_MD5Final (unsigned char digest[16], - my_MD5Context *context); - -C_MODE_END - -#define MY_MD5_HASH(digest,buf,len) \ -do { \ - my_MD5Context ctx; \ - my_MD5Init (&ctx); \ - my_MD5Update (&ctx, buf, len); \ - my_MD5Final (digest, &ctx); \ -} while (0) - -#endif /* defined(HAVE_YASSL) || defined(HAVE_OPENSSL) */ - -C_MODE_START +#ifdef __cplusplus +extern "C" { +#endif void compute_md5_hash(char *digest, const char *buf, int len); -C_MODE_END +/* + Convert an array of bytes to a hexadecimal representation. + + Used to generate a hexadecimal representation of a message digest. +*/ +static inline void array_to_hex(char *to, const unsigned char *str, uint len) +{ + const unsigned char *str_end= str + len; + for (; str != str_end; ++str) + { + *to++= _dig_vec_lower[((uchar) *str) >> 4]; + *to++= _dig_vec_lower[((uchar) *str) & 0x0F]; + } +} + +#ifdef __cplusplus +} +#endif #endif /* MY_MD5_INCLUDED */ diff --git a/include/my_rnd.h b/include/my_rnd.h new file mode 100644 index 00000000000..b4a5d735811 --- /dev/null +++ b/include/my_rnd.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2013 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 or later of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _my_rnd_h +#define _my_rnd_h + +C_MODE_START + +struct my_rnd_struct { + unsigned long seed1,seed2,max_value; + double max_value_dbl; +}; + +void my_rnd_init(struct my_rnd_struct *rand_st, ulong seed1, ulong seed2); +double my_rnd(struct my_rnd_struct *rand_st); +double my_rnd_ssl(struct my_rnd_struct *rand_st); + +C_MODE_END + +#endif /* _my_rnd_h */ diff --git a/include/my_sys.h b/include/my_sys.h index f5f0d61ab46..a29c3653d17 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -174,8 +174,6 @@ extern void *my_memdup(const void *from,size_t length,myf MyFlags); extern char *my_strdup(const char *from,myf MyFlags); extern char *my_strndup(const char *from, size_t length, myf MyFlags); -extern int sf_leaking_memory; /* set to 1 to disable memleak detection */ - #ifdef HAVE_LARGE_PAGES extern uint my_get_large_page_size(void); extern uchar * my_large_malloc(size_t size, myf my_flags); @@ -199,14 +197,18 @@ extern void my_large_free(uchar *ptr); #endif /* GNUC */ #define my_alloca(SZ) alloca((size_t) (SZ)) #define my_afree(PTR) ((void)0) +#define my_safe_alloca(size, max_alloca_sz) ((size <= max_alloca_sz) ? \ + my_alloca(size) : \ + my_malloc(size, MYF(0))) +#define my_safe_afree(ptr, size, max_alloca_sz) if (size > max_alloca_sz) \ + my_free(ptr) #else #define my_alloca(SZ) my_malloc(SZ,MYF(MY_FAE)) #define my_afree(PTR) my_free(PTR) +#define my_safe_alloca(size, max_alloca_sz) my_alloca(size) +#define my_safe_afree(ptr, size, max_alloca_sz) my_afree(ptr) #endif /* HAVE_ALLOCA */ -#define my_safe_alloca(size, min_length) ((size <= min_length) ? my_alloca(size) : my_malloc(size,MYF(MY_FAE))) -#define my_safe_afree(ptr, size, min_length) ((size <= min_length) ? my_afree(ptr) : my_free(ptr)) - #ifndef errno /* did we already get it? */ #ifdef HAVE_ERRNO_AS_DEFINE #include /* errno is a define */ @@ -223,6 +225,7 @@ extern void (*fatal_error_handler_hook)(uint my_err, const char *str, myf MyFlags); extern uint my_file_limit; extern ulonglong my_thread_stack_size; +extern int sf_leaking_memory; /* set to 1 to disable memleak detection */ extern void (*proc_info_hook)(void *, const PSI_stage_info *, PSI_stage_info *, const char *, const char *, const unsigned int); @@ -265,11 +268,6 @@ extern my_bool my_disable_locking, my_disable_async_io, extern my_bool my_disable_sync; extern char wild_many,wild_one,wild_prefix; extern const char *charsets_dir; -/* from default.c */ -extern const char *my_defaults_extra_file; -extern const char *my_defaults_group_suffix; -extern const char *my_defaults_file; - extern my_bool timed_mutexes; enum loglevel { @@ -566,13 +564,8 @@ my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */ typedef uint32 ha_checksum; extern ulong my_crc_dbug_check; -/* Define the type of function to be passed to process_default_option_files */ -typedef int (*Process_option_func)(void *ctx, const char *group_name, - const char *option); - #include - /* Prototypes for mysys and my_func functions */ extern int my_copy(const char *from,const char *to,myf MyFlags); @@ -631,6 +624,13 @@ extern int my_access(const char *path, int amode); extern int check_if_legal_filename(const char *path); extern int check_if_legal_tablename(const char *path); +#ifdef __WIN__ +extern my_bool is_filename_allowed(const char *name, size_t length, + my_bool allow_current_dir); +#else /* __WIN__ */ +# define is_filename_allowed(name, length, allow_cwd) (TRUE) +#endif /* __WIN__ */ + #ifdef _WIN32 /* Windows-only functions (CRT equivalents)*/ extern HANDLE my_get_osfhandle(File fd); @@ -656,15 +656,16 @@ extern void thr_set_sync_wait_callback(void (*before_sync)(void), extern int my_sync(File fd, myf my_flags); extern int my_sync_dir(const char *dir_name, myf my_flags); extern int my_sync_dir_by_file(const char *file_name, myf my_flags); -extern void my_error(int nr,myf MyFlags, ...); +extern const char *my_get_err_msg(uint nr); +extern void my_error(uint nr,myf MyFlags, ...); extern void my_printf_error(uint my_err, const char *format, myf MyFlags, ...) ATTRIBUTE_FORMAT(printf, 2, 4); extern void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap); extern int my_error_register(const char** (*get_errmsgs) (), - int first, int last); -extern const char **my_error_unregister(int first, int last); + uint first, uint last); +extern const char **my_error_unregister(uint first, uint last); extern void my_message(uint my_err, const char *str,myf MyFlags); extern void my_message_stderr(uint my_err, const char *str, myf MyFlags); extern my_bool my_init(void); @@ -853,22 +854,6 @@ static inline char *safe_strdup_root(MEM_ROOT *root, const char *str) } extern char *strmake_root(MEM_ROOT *root,const char *str,size_t len); extern void *memdup_root(MEM_ROOT *root,const void *str, size_t len); -extern int get_defaults_options(int argc, char **argv, - char **defaults, char **extra_defaults, - char **group_suffix); -extern my_bool my_getopt_use_args_separator; -extern my_bool my_getopt_is_args_separator(const char* arg); -extern int my_load_defaults(const char *conf_file, const char **groups, - int *argc, char ***argv, const char ***); -extern int load_defaults(const char *conf_file, const char **groups, - int *argc, char ***argv); -extern int my_search_option_files(const char *conf_file, int *argc, - char ***argv, uint *args_used, - Process_option_func func, void *func_ctx, - const char **default_directories); -extern void free_defaults(char **argv); -extern void my_print_default_files(const char *conf_file); -extern void print_defaults(const char *conf_file, const char **groups); extern my_bool my_compress(uchar *, size_t *, size_t *); extern my_bool my_uncompress(uchar *, size_t , size_t *); extern uchar *my_compress_alloc(const uchar *packet, size_t *len, @@ -960,14 +945,6 @@ void my_uuid(uchar *guid); void my_uuid2str(const uchar *guid, char *s); void my_uuid_end(); -struct my_rnd_struct { - unsigned long seed1,seed2,max_value; - double max_value_dbl; -}; - -void my_rnd_init(struct my_rnd_struct *rand_st, ulong seed1, ulong seed2); -double my_rnd(struct my_rnd_struct *rand_st); - /* character sets */ extern uint get_charset_number(const char *cs_name, uint cs_flags); extern uint get_collation_number(const char *name); @@ -1030,6 +1007,5 @@ void my_init_mysys_psi_keys(void); struct st_mysql_file; extern struct st_mysql_file *mysql_stdin; - C_MODE_END #endif /* _my_sys_h */ diff --git a/include/my_time.h b/include/my_time.h index ea471fd6b0a..4991d996258 100644 --- a/include/my_time.h +++ b/include/my_time.h @@ -146,8 +146,8 @@ void my_init_time(void); estimate. RETURN VALUES - FALSE The value seems sane - TRUE The MYSQL_TIME value is definitely out of range + TRUE The value seems sane + FALSE The MYSQL_TIME value is definitely out of range */ static inline my_bool validate_timestamp_range(const MYSQL_TIME *t) diff --git a/include/myisammrg.h b/include/myisammrg.h index 84b2d637892..89293537989 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -31,7 +31,8 @@ extern "C" { #include -#define MYRG_NAME_EXT ".MRG" +#define MYRG_NAME_EXT ".MRG" +#define MYRG_NAME_TMPEXT ".MRG_TMP" /* In which table to INSERT rows */ #define MERGE_INSERT_DISABLED 0 diff --git a/include/mysql/client_authentication.h b/include/mysql/client_authentication.h new file mode 100644 index 00000000000..2bd2fc98bac --- /dev/null +++ b/include/mysql/client_authentication.h @@ -0,0 +1,13 @@ +#ifndef CLIENT_AUTHENTICATION_H +#define CLIENT_AUTHENTICATION_H +#include "mysql.h" +#include "mysql/client_plugin.h" + +C_MODE_START +int sha256_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +int sha256_password_init(char *, size_t, int, va_list); +int sha256_password_deinit(void); +C_MODE_END + +#endif + diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index a96e3e72b7b..7092739f5d1 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -46,6 +46,7 @@ class Item; #endif typedef char my_bool; +typedef void * MYSQL_PLUGIN; #include @@ -71,10 +72,10 @@ typedef struct st_mysql_xid MYSQL_XID; */ /* MySQL plugin interface version */ -#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0103 +#define MYSQL_PLUGIN_INTERFACE_VERSION 0x0104 /* MariaDB plugin interface version */ -#define MARIA_PLUGIN_INTERFACE_VERSION 0x0105 +#define MARIA_PLUGIN_INTERFACE_VERSION 0x0107 /* The allowable types of plugins @@ -87,7 +88,8 @@ typedef struct st_mysql_xid MYSQL_XID; #define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */ #define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */ #define MYSQL_AUTHENTICATION_PLUGIN 7 /* The authentication plugin type */ -#define MYSQL_MAX_PLUGIN_TYPE_NUM 8 /* The number of plugin types */ +#define MYSQL_VALIDATE_PASSWORD_PLUGIN 8 /* validate password plugin type */ +#define MYSQL_MAX_PLUGIN_TYPE_NUM 9 /* The number of plugin types */ /* We use the following strings to define licenses for plugins */ #define PLUGIN_LICENSE_PROPRIETARY 0 @@ -560,7 +562,7 @@ struct handlerton; /* API for Replication plugin. (MYSQL_REPLICATION_PLUGIN) */ - #define MYSQL_REPLICATION_INTERFACE_VERSION 0x0100 + #define MYSQL_REPLICATION_INTERFACE_VERSION 0x0200 /** Replication plugin descriptor @@ -608,6 +610,7 @@ int thd_sql_command(const MYSQL_THD thd); void **thd_ha_data(const MYSQL_THD thd, const struct handlerton *hton); void thd_storage_lock_wait(MYSQL_THD thd, long long value); int thd_tx_isolation(const MYSQL_THD thd); +int thd_tx_is_read_only(const MYSQL_THD thd); char *thd_security_context(MYSQL_THD thd, char *buffer, unsigned int length, unsigned int max_query_len); /* Increments the row counter, see THD::row_count */ diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp index a3fabf011ab..ff82ad951ea 100644 --- a/include/mysql/plugin_audit.h.pp +++ b/include/mysql/plugin_audit.h.pp @@ -1,5 +1,6 @@ #include "plugin.h" typedef char my_bool; +typedef void * MYSQL_PLUGIN; #include #include extern struct my_snprintf_service_st { @@ -107,6 +108,13 @@ extern struct thd_timezone_service_st { } *thd_timezone_service; my_time_t thd_TIME_to_gmt_sec(void* thd, const MYSQL_TIME *ltime, unsigned int *errcode); void thd_gmt_sec_to_TIME(void* thd, MYSQL_TIME *ltime, my_time_t t); +#include +extern struct my_sha1_service_st { + void (*my_sha1_type)(unsigned char*, const char*, size_t); + void (*my_sha1_multi_type)(unsigned char*, ...); +} *my_sha1_service; +void my_sha1(unsigned char*, const char*, size_t); +void my_sha1_multi(unsigned char*, ...); struct st_mysql_xid { long formatID; long gtrid_length; @@ -247,6 +255,7 @@ int thd_sql_command(const void* thd); void **thd_ha_data(const void* thd, const struct handlerton *hton); void thd_storage_lock_wait(void* thd, long long value); int thd_tx_isolation(const void* thd); +int thd_tx_is_read_only(const void* thd); char *thd_security_context(void* thd, char *buffer, unsigned int length, unsigned int max_query_len); void thd_inc_row_count(void* thd); diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp index 28172286fb3..5f7c1ab72f1 100644 --- a/include/mysql/plugin_auth.h.pp +++ b/include/mysql/plugin_auth.h.pp @@ -1,5 +1,6 @@ #include typedef char my_bool; +typedef void * MYSQL_PLUGIN; #include #include extern struct my_snprintf_service_st { @@ -107,6 +108,13 @@ extern struct thd_timezone_service_st { } *thd_timezone_service; my_time_t thd_TIME_to_gmt_sec(void* thd, const MYSQL_TIME *ltime, unsigned int *errcode); void thd_gmt_sec_to_TIME(void* thd, MYSQL_TIME *ltime, my_time_t t); +#include +extern struct my_sha1_service_st { + void (*my_sha1_type)(unsigned char*, const char*, size_t); + void (*my_sha1_multi_type)(unsigned char*, ...); +} *my_sha1_service; +void my_sha1(unsigned char*, const char*, size_t); +void my_sha1_multi(unsigned char*, ...); struct st_mysql_xid { long formatID; long gtrid_length; @@ -247,6 +255,7 @@ int thd_sql_command(const void* thd); void **thd_ha_data(const void* thd, const struct handlerton *hton); void thd_storage_lock_wait(void* thd, long long value); int thd_tx_isolation(const void* thd); +int thd_tx_is_read_only(const void* thd); char *thd_security_context(void* thd, char *buffer, unsigned int length, unsigned int max_query_len); void thd_inc_row_count(void* thd); diff --git a/include/mysql/plugin_auth_common.h b/include/mysql/plugin_auth_common.h index c0b61730d0d..9d7dd2a08bf 100644 --- a/include/mysql/plugin_auth_common.h +++ b/include/mysql/plugin_auth_common.h @@ -34,6 +34,27 @@ return values of the plugin authenticate_user() method. */ + /** + Authentication failed, plugin internal error. + An error occurred in the authentication plugin itself. + These errors are reported in table performance_schema.host_cache, + column COUNT_AUTH_PLUGIN_ERRORS. +*/ +#define CR_AUTH_PLUGIN_ERROR 3 +/** + Authentication failed, client server handshake. + An error occurred during the client server handshake. + These errors are reported in table performance_schema.host_cache, + column COUNT_HANDSHAKE_ERRORS. +*/ +#define CR_AUTH_HANDSHAKE 2 +/** + Authentication failed, user credentials. + For example, wrong passwords. + These errors are reported in table performance_schema.host_cache, + column COUNT_AUTHENTICATION_ERRORS. +*/ +#define CR_AUTH_USER_CREDENTIALS 1 /** Authentication failed. Additionally, all other CR_xxx values (libmysql error code) can be used too. diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp index 532e049cf53..05eed030d66 100644 --- a/include/mysql/plugin_ftparser.h.pp +++ b/include/mysql/plugin_ftparser.h.pp @@ -1,5 +1,6 @@ #include "plugin.h" typedef char my_bool; +typedef void * MYSQL_PLUGIN; #include #include extern struct my_snprintf_service_st { @@ -107,6 +108,13 @@ extern struct thd_timezone_service_st { } *thd_timezone_service; my_time_t thd_TIME_to_gmt_sec(void* thd, const MYSQL_TIME *ltime, unsigned int *errcode); void thd_gmt_sec_to_TIME(void* thd, MYSQL_TIME *ltime, my_time_t t); +#include +extern struct my_sha1_service_st { + void (*my_sha1_type)(unsigned char*, const char*, size_t); + void (*my_sha1_multi_type)(unsigned char*, ...); +} *my_sha1_service; +void my_sha1(unsigned char*, const char*, size_t); +void my_sha1_multi(unsigned char*, ...); struct st_mysql_xid { long formatID; long gtrid_length; @@ -200,6 +208,7 @@ int thd_sql_command(const void* thd); void **thd_ha_data(const void* thd, const struct handlerton *hton); void thd_storage_lock_wait(void* thd, long long value); int thd_tx_isolation(const void* thd); +int thd_tx_is_read_only(const void* thd); char *thd_security_context(void* thd, char *buffer, unsigned int length, unsigned int max_query_len); void thd_inc_row_count(void* thd); diff --git a/include/mysql/psi/mysql_file.h b/include/mysql/psi/mysql_file.h index 816ac713631..c226258f462 100644 --- a/include/mysql/psi/mysql_file.h +++ b/include/mysql/psi/mysql_file.h @@ -518,7 +518,7 @@ static inline void inline_mysql_file_register( ) { #ifdef HAVE_PSI_FILE_INTERFACE - PSI_CALL(register_file)(category, info, count); + PSI_FILE_CALL(register_file)(category, info, count); #endif } @@ -533,13 +533,13 @@ inline_mysql_file_fgets( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_READ); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_READ); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) size, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) size, src_file, src_line); result= fgets(str, size, file->m_file); - PSI_CALL(end_file_wait)(locker, result ? strlen(result) : 0); + PSI_FILE_CALL(end_file_wait)(locker, result ? strlen(result) : 0); return result; } #endif @@ -559,13 +559,13 @@ inline_mysql_file_fgetc( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_READ); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_READ); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 1, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 1, src_file, src_line); result= fgetc(file->m_file); - PSI_CALL(end_file_wait)(locker, (size_t) 1); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 1); return result; } #endif @@ -586,14 +586,14 @@ inline_mysql_file_fputs( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_WRITE); if (likely(locker != NULL)) { bytes= str ? strlen(str) : 0; - PSI_CALL(start_file_wait)(locker, bytes, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, bytes, src_file, src_line); result= fputs(str, file->m_file); - PSI_CALL(end_file_wait)(locker, bytes); + PSI_FILE_CALL(end_file_wait)(locker, bytes); return result; } #endif @@ -613,13 +613,13 @@ inline_mysql_file_fputc( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 1, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 1, src_file, src_line); result= fputc(c, file->m_file); - PSI_CALL(end_file_wait)(locker, (size_t) 1); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 1); return result; } #endif @@ -639,15 +639,15 @@ inline_mysql_file_fprintf(MYSQL_FILE *file, const char *format, ...) #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, __FILE__, __LINE__); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, __FILE__, __LINE__); va_start(args, format); result= vfprintf(file->m_file, format, args); va_end(args); - PSI_CALL(end_file_wait)(locker, (size_t) result); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) result); return result; } #endif @@ -669,13 +669,13 @@ inline_mysql_file_vfprintf( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= vfprintf(file->m_file, format, args); - PSI_CALL(end_file_wait)(locker, (size_t) result); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) result); return result; } #endif @@ -695,13 +695,13 @@ inline_mysql_file_fflush( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_FLUSH); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_FLUSH); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= fflush(file->m_file); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -727,13 +727,13 @@ inline_mysql_file_fstat( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, filenr, - PSI_FILE_FSTAT); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, filenr, PSI_FILE_FSTAT); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_fstat(filenr, stat_area, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -753,14 +753,13 @@ inline_mysql_file_stat( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, - key, PSI_FILE_STAT, - path, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_STAT, path, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_open_wait)(locker, src_file, src_line); + PSI_FILE_CALL(start_file_open_wait)(locker, src_file, src_line); result= my_stat(path, stat_area, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_open_wait)(locker, result); return result; } #endif @@ -780,14 +779,14 @@ inline_mysql_file_chsize( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, - PSI_FILE_CHSIZE); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_CHSIZE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) newlength, src_file, + PSI_FILE_CALL(start_file_wait)(locker, (size_t) newlength, src_file, src_line); result= my_chsize(file, newlength, filler, flags); - PSI_CALL(end_file_wait)(locker, (size_t) newlength); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) newlength); return result; } #endif @@ -810,14 +809,14 @@ inline_mysql_file_fopen( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker) + locker= PSI_FILE_CALL(get_thread_file_name_locker) (&state, key, PSI_FILE_STREAM_OPEN, filename, that); if (likely(locker != NULL)) { - that->m_psi= PSI_CALL(start_file_open_wait)(locker, src_file, - src_line); + PSI_FILE_CALL(start_file_open_wait) + (locker, src_file, src_line); that->m_file= my_fopen(filename, flags, myFlags); - PSI_CALL(end_file_open_wait)(locker); + that->m_psi= PSI_FILE_CALL(end_file_open_wait)(locker, that->m_file); if (unlikely(that->m_file == NULL)) { my_free(that); @@ -851,13 +850,13 @@ inline_mysql_file_fclose( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_STREAM_CLOSE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_STREAM_CLOSE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line); result= my_fclose(file->m_file, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_close_wait)(locker, result); my_free(file); return result; } @@ -881,17 +880,17 @@ inline_mysql_file_fread( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_read; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_READ); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_READ); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_fread(file->m_file, buffer, count, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_read= (result == 0) ? count : 0; else bytes_read= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_read); + PSI_FILE_CALL(end_file_wait)(locker, bytes_read); return result; } #endif @@ -912,17 +911,17 @@ inline_mysql_file_fwrite( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_written; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_fwrite(file->m_file, buffer, count, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_written= (result == 0) ? count : 0; else bytes_written= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_written); + PSI_FILE_CALL(end_file_wait)(locker, bytes_written); return result; } #endif @@ -942,13 +941,13 @@ inline_mysql_file_fseek( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_SEEK); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_SEEK); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_fseek(file->m_file, pos, whence, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -968,13 +967,13 @@ inline_mysql_file_ftell( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_stream_locker)(&state, file->m_psi, - PSI_FILE_TELL); + locker= PSI_FILE_CALL(get_thread_file_stream_locker) + (&state, file->m_psi, PSI_FILE_TELL); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_ftell(file->m_file, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -994,13 +993,13 @@ inline_mysql_file_create( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_CREATE, - filename, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_CREATE, filename, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_open_wait)(locker, src_file, src_line); + PSI_FILE_CALL(start_file_open_wait)(locker, src_file, src_line); file= my_create(filename, create_flags, access_flags, myFlags); - PSI_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); + PSI_FILE_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); return file; } #endif @@ -1024,7 +1023,7 @@ inline_mysql_file_create_temp( */ file= create_temp_file(to, dir, pfx, mode, myFlags); #ifdef HAVE_PSI_FILE_INTERFACE - PSI_CALL(create_file)(key, to, file); + PSI_FILE_CALL(create_file)(key, to, file); #endif return file; } @@ -1040,13 +1039,13 @@ inline_mysql_file_open( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_OPEN, - filename, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_OPEN, filename, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_open_wait)(locker, src_file, src_line); + PSI_FILE_CALL(start_file_open_wait)(locker, src_file, src_line); file= my_open(filename, flags, myFlags); - PSI_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); + PSI_FILE_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); return file; } #endif @@ -1066,13 +1065,13 @@ inline_mysql_file_close( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, - PSI_FILE_CLOSE); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_CLOSE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line); result= my_close(file, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_close_wait)(locker, result); return result; } #endif @@ -1093,17 +1092,17 @@ inline_mysql_file_read( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_read; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, - PSI_FILE_READ); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_READ); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_read(file, buffer, count, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_read= (result == 0) ? count : 0; else bytes_read= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_read); + PSI_FILE_CALL(end_file_wait)(locker, bytes_read); return result; } #endif @@ -1124,17 +1123,17 @@ inline_mysql_file_write( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_written; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_write(file, buffer, count, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_written= (result == 0) ? count : 0; else bytes_written= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_written); + PSI_FILE_CALL(end_file_wait)(locker, bytes_written); return result; } #endif @@ -1155,16 +1154,17 @@ inline_mysql_file_pread( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_read; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, PSI_FILE_READ); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_READ); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_pread(file, buffer, count, offset, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_read= (result == 0) ? count : 0; else bytes_read= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_read); + PSI_FILE_CALL(end_file_wait)(locker, bytes_read); return result; } #endif @@ -1185,17 +1185,17 @@ inline_mysql_file_pwrite( struct PSI_file_locker *locker; PSI_file_locker_state state; size_t bytes_written; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, - PSI_FILE_WRITE); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_WRITE); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, count, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, count, src_file, src_line); result= my_pwrite(file, buffer, count, offset, flags); if (flags & (MY_NABP | MY_FNABP)) bytes_written= (result == 0) ? count : 0; else bytes_written= (result != MY_FILE_ERROR) ? result : 0; - PSI_CALL(end_file_wait)(locker, bytes_written); + PSI_FILE_CALL(end_file_wait)(locker, bytes_written); return result; } #endif @@ -1215,12 +1215,13 @@ inline_mysql_file_seek( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, PSI_FILE_SEEK); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_SEEK); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_seek(file, pos, whence, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -1240,12 +1241,13 @@ inline_mysql_file_tell( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, file, PSI_FILE_TELL); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, file, PSI_FILE_TELL); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_tell(file, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -1265,13 +1267,13 @@ inline_mysql_file_delete( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_DELETE, - name, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_DELETE, name, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line); result= my_delete(name, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_close_wait)(locker, result); return result; } #endif @@ -1291,13 +1293,13 @@ inline_mysql_file_rename( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_RENAME, - to, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_RENAME, to, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_rename(from, to, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -1318,14 +1320,14 @@ inline_mysql_file_create_with_symlink( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_CREATE, - filename, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_CREATE, filename, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_open_wait)(locker, src_file, src_line); + PSI_FILE_CALL(start_file_open_wait)(locker, src_file, src_line); file= my_create_with_symlink(linkname, filename, create_flags, access_flags, flags); - PSI_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); + PSI_FILE_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file); return file; } #endif @@ -1346,13 +1348,13 @@ inline_mysql_file_delete_with_symlink( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_DELETE, - name, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_DELETE, name, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line); result= my_delete_with_symlink(name, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_close_wait)(locker, result); return result; } #endif @@ -1372,13 +1374,13 @@ inline_mysql_file_rename_with_symlink( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_name_locker)(&state, key, PSI_FILE_RENAME, - to, &locker); + locker= PSI_FILE_CALL(get_thread_file_name_locker) + (&state, key, PSI_FILE_RENAME, to, &locker); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_rename_with_symlink(from, to, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif @@ -1398,12 +1400,13 @@ inline_mysql_file_sync( #ifdef HAVE_PSI_FILE_INTERFACE struct PSI_file_locker *locker; PSI_file_locker_state state; - locker= PSI_CALL(get_thread_file_descriptor_locker)(&state, fd, PSI_FILE_SYNC); + locker= PSI_FILE_CALL(get_thread_file_descriptor_locker) + (&state, fd, PSI_FILE_SYNC); if (likely(locker != NULL)) { - PSI_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); + PSI_FILE_CALL(start_file_wait)(locker, (size_t) 0, src_file, src_line); result= my_sync(fd, flags); - PSI_CALL(end_file_wait)(locker, (size_t) 0); + PSI_FILE_CALL(end_file_wait)(locker, (size_t) 0); return result; } #endif diff --git a/include/mysql/psi/mysql_idle.h b/include/mysql/psi/mysql_idle.h index 7a3fccfdb8c..c53d0ceb8c7 100644 --- a/include/mysql/psi/mysql_idle.h +++ b/include/mysql/psi/mysql_idle.h @@ -70,7 +70,7 @@ inline_mysql_start_idle_wait(PSI_idle_locker_state *state, const char *src_file, int src_line) { struct PSI_idle_locker *locker; - locker= PSI_CALL(start_idle_wait)(state, src_file, src_line); + locker= PSI_IDLE_CALL(start_idle_wait)(state, src_file, src_line); return locker; } @@ -82,7 +82,7 @@ static inline void inline_mysql_end_idle_wait(struct PSI_idle_locker *locker) { if (likely(locker != NULL)) - PSI_CALL(end_idle_wait)(locker); + PSI_IDLE_CALL(end_idle_wait)(locker); } #endif diff --git a/include/mysql/psi/mysql_socket.h b/include/mysql/psi/mysql_socket.h index c908032883a..e1d56539f85 100644 --- a/include/mysql/psi/mysql_socket.h +++ b/include/mysql/psi/mysql_socket.h @@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA #ifdef __WIN__ #include #include + #include #define SOCKBUF_T char #else #include @@ -121,7 +122,7 @@ mysql_socket_set_address( { #ifdef HAVE_PSI_SOCKET_INTERFACE if (socket.m_psi != NULL) - PSI_CALL(set_socket_info)(socket.m_psi, NULL, addr, addr_len); + PSI_SOCKET_CALL(set_socket_info)(socket.m_psi, NULL, addr, addr_len); #endif } @@ -141,7 +142,7 @@ MYSQL_SOCKET socket __attribute__ ((unused)) { #ifdef HAVE_PSI_SOCKET_INTERFACE if (socket.m_psi != NULL) - PSI_CALL(set_socket_thread_owner)(socket.m_psi); + PSI_SOCKET_CALL(set_socket_thread_owner)(socket.m_psi); #endif } @@ -247,8 +248,8 @@ inline_mysql_start_socket_wait(PSI_socket_locker_state *state, struct PSI_socket_locker *locker; if (mysql_socket.m_psi != NULL) { - locker= PSI_CALL(start_socket_wait)(state, mysql_socket.m_psi, op, - byte_count, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (state, mysql_socket.m_psi, op, byte_count, src_file, src_line); } else locker= NULL; @@ -263,7 +264,7 @@ static inline void inline_mysql_end_socket_wait(struct PSI_socket_locker *locker, size_t byte_count) { if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, byte_count); + PSI_SOCKET_CALL(end_socket_wait)(locker, byte_count); } /** @@ -276,7 +277,7 @@ static inline void inline_mysql_socket_set_state(MYSQL_SOCKET socket, enum PSI_socket_state state) { if (socket.m_psi != NULL) - PSI_CALL(set_socket_state)(socket.m_psi, state); + PSI_SOCKET_CALL(set_socket_state)(socket.m_psi, state); } #endif /* HAVE_PSI_SOCKET_INTERFACE */ @@ -537,7 +538,7 @@ static inline void inline_mysql_socket_register( PSI_socket_info *info, int count) { - PSI_CALL(register_socket)(category, info, count); + PSI_SOCKET_CALL(register_socket)(category, info, count); } #endif @@ -551,16 +552,15 @@ inline_mysql_socket_socket #endif int domain, int type, int protocol) { - MYSQL_SOCKET mysql_socket; + MYSQL_SOCKET mysql_socket= MYSQL_INVALID_SOCKET; mysql_socket.fd= socket(domain, type, protocol); #ifdef HAVE_PSI_SOCKET_INTERFACE - mysql_socket.m_psi= PSI_CALL(init_socket)(key, (const my_socket*)&mysql_socket.fd); - - if (likely(mysql_socket.fd != INVALID_SOCKET && mysql_socket.m_psi != NULL)) - PSI_CALL(set_socket_info)(mysql_socket.m_psi, &mysql_socket.fd, NULL, 0); -#else - mysql_socket.m_psi= NULL; + if (likely(mysql_socket.fd != INVALID_SOCKET)) + { + mysql_socket.m_psi= PSI_SOCKET_CALL(init_socket) + (key, (const my_socket*)&mysql_socket.fd, NULL, 0); + } #endif return mysql_socket; } @@ -583,17 +583,18 @@ inline_mysql_socket_bind /* Instrumentation start */ PSI_socket_locker_state state; PSI_socket_locker *locker; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_BIND, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_BIND, (size_t)0, src_file, src_line); /* Instrumented code */ result= bind(mysql_socket.fd, addr, len); /* Instrumentation end */ - PSI_CALL(set_socket_info)(mysql_socket.m_psi, NULL, addr, len); + if (result == 0) + PSI_SOCKET_CALL(set_socket_info)(mysql_socket.m_psi, NULL, addr, len); if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -622,15 +623,15 @@ inline_mysql_socket_getsockname /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_BIND, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_BIND, (size_t)0, src_file, src_line); /* Instrumented code */ result= getsockname(mysql_socket.fd, addr, len); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -660,15 +661,15 @@ inline_mysql_socket_connect /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); /* Instrumented code */ result= connect(mysql_socket.fd, addr, len); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -698,15 +699,15 @@ inline_mysql_socket_getpeername /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_BIND, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_BIND, (size_t)0, src_file, src_line); /* Instrumented code */ result= getpeername(mysql_socket.fd, addr, len); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -736,18 +737,18 @@ inline_mysql_socket_send /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_SEND, n, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_SEND, n, src_file, src_line); /* Instrumented code */ - result= send(mysql_socket.fd, buf, n, flags); + result= send(mysql_socket.fd, buf, IF_WIN((int),) n, flags); /* Instrumentation end */ if (locker != NULL) { size_t bytes_written; bytes_written= (result > -1) ? result : 0; - PSI_CALL(end_socket_wait)(locker, bytes_written); + PSI_SOCKET_CALL(end_socket_wait)(locker, bytes_written); } return result; @@ -755,7 +756,7 @@ inline_mysql_socket_send #endif /* Non instrumented code */ - result= send(mysql_socket.fd, buf, n, flags); + result= send(mysql_socket.fd, buf, IF_WIN((int),) n, flags); return result; } @@ -778,18 +779,18 @@ inline_mysql_socket_recv /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_RECV, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_RECV, (size_t)0, src_file, src_line); /* Instrumented code */ - result= recv(mysql_socket.fd, buf, n, flags); + result= recv(mysql_socket.fd, buf, IF_WIN((int),) n, flags); /* Instrumentation end */ if (locker != NULL) { size_t bytes_read; bytes_read= (result > -1) ? result : 0; - PSI_CALL(end_socket_wait)(locker, bytes_read); + PSI_SOCKET_CALL(end_socket_wait)(locker, bytes_read); } return result; @@ -797,7 +798,7 @@ inline_mysql_socket_recv #endif /* Non instrumented code */ - result= recv(mysql_socket.fd, buf, n, flags); + result= recv(mysql_socket.fd, buf, IF_WIN((int),) n, flags); return result; } @@ -820,18 +821,18 @@ inline_mysql_socket_sendto /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_SEND, n, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_SEND, n, src_file, src_line); /* Instrumented code */ - result= sendto(mysql_socket.fd, buf, n, flags, addr, addr_len); + result= sendto(mysql_socket.fd, buf, IF_WIN((int),) n, flags, addr, addr_len); /* Instrumentation end */ if (locker != NULL) { size_t bytes_written; bytes_written = (result > -1) ? result : 0; - PSI_CALL(end_socket_wait)(locker, bytes_written); + PSI_SOCKET_CALL(end_socket_wait)(locker, bytes_written); } return result; @@ -839,7 +840,7 @@ inline_mysql_socket_sendto #endif /* Non instrumented code */ - result= sendto(mysql_socket.fd, buf, n, flags, addr, addr_len); + result= sendto(mysql_socket.fd, buf, IF_WIN((int),) n, flags, addr, addr_len); return result; } @@ -863,18 +864,18 @@ inline_mysql_socket_recvfrom /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_RECV, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_RECV, (size_t)0, src_file, src_line); /* Instrumented code */ - result= recvfrom(mysql_socket.fd, buf, n, flags, addr, addr_len); + result= recvfrom(mysql_socket.fd, buf, IF_WIN((int),) n, flags, addr, addr_len); /* Instrumentation end */ if (locker != NULL) { size_t bytes_read; bytes_read = (result > -1) ? result : 0; - PSI_CALL(end_socket_wait)(locker, bytes_read); + PSI_SOCKET_CALL(end_socket_wait)(locker, bytes_read); } return result; @@ -882,7 +883,7 @@ inline_mysql_socket_recvfrom #endif /* Non instrumented code */ - result= recvfrom(mysql_socket.fd, buf, n, flags, addr, addr_len); + result= recvfrom(mysql_socket.fd, buf, IF_WIN((int),) n, flags, addr, addr_len); return result; } @@ -905,15 +906,15 @@ inline_mysql_socket_getsockopt /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_OPT, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_OPT, (size_t)0, src_file, src_line); /* Instrumented code */ result= getsockopt(mysql_socket.fd, level, optname, optval, optlen); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -944,15 +945,15 @@ inline_mysql_socket_setsockopt /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_OPT, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_OPT, (size_t)0, src_file, src_line); /* Instrumented code */ result= setsockopt(mysql_socket.fd, level, optname, optval, optlen); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -982,15 +983,15 @@ inline_mysql_socket_listen /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); /* Instrumented code */ result= listen(mysql_socket.fd, backlog); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } @@ -1021,15 +1022,15 @@ inline_mysql_socket_accept /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, socket_listen.m_psi, - PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, socket_listen.m_psi, PSI_SOCKET_CONNECT, (size_t)0, src_file, src_line); /* Instrumented code */ socket_accept.fd= accept(socket_listen.fd, addr, &addr_length); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); } else #endif @@ -1039,14 +1040,12 @@ inline_mysql_socket_accept } #ifdef HAVE_PSI_SOCKET_INTERFACE - /* Initialize the instrument with the new socket descriptor and address */ - socket_accept.m_psi= - PSI_CALL(init_socket)(key, (const my_socket*)&socket_accept.fd); - - /* FIXME: simplify this with just 1 call to init_socket(). */ - if (socket_accept.m_psi != NULL) - PSI_CALL(set_socket_info)(socket_accept.m_psi, &socket_accept.fd, addr, - addr_length); + if (likely(socket_accept.fd != INVALID_SOCKET)) + { + /* Initialize the instrument with the new socket descriptor and address */ + socket_accept.m_psi= PSI_SOCKET_CALL(init_socket) + (key, (const my_socket*)&socket_accept.fd, addr, addr_length); + } #endif return socket_accept; @@ -1070,18 +1069,18 @@ inline_mysql_socket_close /* Instrumentation start */ PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_CLOSE, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_CLOSE, (size_t)0, src_file, src_line); /* Instrumented code */ result= closesocket(mysql_socket.fd); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); /* Remove the instrumentation for this socket. */ if (mysql_socket.m_psi != NULL) - PSI_CALL(destroy_socket)(mysql_socket.m_psi); + PSI_SOCKET_CALL(destroy_socket)(mysql_socket.m_psi); return result; } @@ -1105,28 +1104,53 @@ inline_mysql_socket_shutdown { int result; - /* Instrumentation start */ +#ifdef __WIN__ + static LPFN_DISCONNECTEX DisconnectEx = NULL; + if (DisconnectEx == NULL) + { + DWORD dwBytesReturned; + GUID guidDisconnectEx = WSAID_DISCONNECTEX; + WSAIoctl(mysql_socket.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidDisconnectEx, sizeof(GUID), + &DisconnectEx, sizeof(DisconnectEx), + &dwBytesReturned, NULL, NULL); + } +#endif + +/* Instrumentation start */ #ifdef HAVE_PSI_SOCKET_INTERFACE if (mysql_socket.m_psi != NULL) { PSI_socket_locker *locker; PSI_socket_locker_state state; - locker= PSI_CALL(start_socket_wait)(&state, mysql_socket.m_psi, - PSI_SOCKET_SHUTDOWN, (size_t)0, src_file, src_line); + locker= PSI_SOCKET_CALL(start_socket_wait) + (&state, mysql_socket.m_psi, PSI_SOCKET_SHUTDOWN, (size_t)0, src_file, src_line); /* Instrumented code */ - result= shutdown(mysql_socket.fd, how); +#ifdef __WIN__ + if (DisconnectEx) + result= (DisconnectEx(mysql_socket.fd, (LPOVERLAPPED) NULL, + (DWORD) 0, (DWORD) 0) == TRUE) ? 0 : -1; + else +#endif + result= shutdown(mysql_socket.fd, how); /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_socket_wait)(locker, (size_t)0); + PSI_SOCKET_CALL(end_socket_wait)(locker, (size_t)0); return result; } #endif /* Non instrumented code */ - result= shutdown(mysql_socket.fd, how); +#ifdef __WIN__ + if (DisconnectEx) + result= (DisconnectEx(mysql_socket.fd, (LPOVERLAPPED) NULL, + (DWORD) 0, (DWORD) 0) == TRUE) ? 0 : -1; + else +#endif + result= shutdown(mysql_socket.fd, how); return result; } diff --git a/include/mysql/psi/mysql_stage.h b/include/mysql/psi/mysql_stage.h index dc44e9b0bed..61bfdbb7d59 100644 --- a/include/mysql/psi/mysql_stage.h +++ b/include/mysql/psi/mysql_stage.h @@ -53,7 +53,7 @@ static inline void inline_mysql_stage_register( const char *category, PSI_stage_info **info, int count) { - PSI_CALL(register_stage)(category, info, count); + PSI_STAGE_CALL(register_stage)(category, info, count); } #endif @@ -62,7 +62,7 @@ static inline void inline_mysql_set_stage(PSI_stage_key key, const char *src_file, int src_line) { - PSI_CALL(start_stage)(key, src_file, src_line); + PSI_STAGE_CALL(start_stage)(key, src_file, src_line); } #endif diff --git a/include/mysql/psi/mysql_statement.h b/include/mysql/psi/mysql_statement.h index 1b065065e57..d7a76ee25e4 100644 --- a/include/mysql/psi/mysql_statement.h +++ b/include/mysql/psi/mysql_statement.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -63,10 +63,10 @@ #endif #ifdef HAVE_PSI_STATEMENT_INTERFACE - #define MYSQL_START_STATEMENT(STATE, K, DB, DB_LEN) \ - inline_mysql_start_statement(STATE, K, DB, DB_LEN, __FILE__, __LINE__) + #define MYSQL_START_STATEMENT(STATE, K, DB, DB_LEN, CS) \ + inline_mysql_start_statement(STATE, K, DB, DB_LEN, CS, __FILE__, __LINE__) #else - #define MYSQL_START_STATEMENT(STATE, K, DB, DB_LEN) \ + #define MYSQL_START_STATEMENT(STATE, K, DB, DB_LEN, CS) \ NULL #endif @@ -122,7 +122,7 @@ static inline void inline_mysql_statement_register( const char *category, PSI_statement_info *info, int count) { - PSI_CALL(register_statement)(category, info, count); + PSI_STATEMENT_CALL(register_statement)(category, info, count); } #ifdef HAVE_PSI_STATEMENT_DIGEST_INTERFACE @@ -132,7 +132,7 @@ inline_mysql_digest_start(PSI_statement_locker *locker) PSI_digest_locker* digest_locker= NULL; if (likely(locker != NULL)) - digest_locker= PSI_CALL(digest_start)(locker); + digest_locker= PSI_STATEMENT_CALL(digest_start)(locker); return digest_locker; } #endif @@ -143,7 +143,7 @@ inline_mysql_add_token(PSI_digest_locker *locker, uint token, void *yylval) { if (likely(locker != NULL)) - locker= PSI_CALL(digest_add_token)(locker, token, + locker= PSI_STATEMENT_CALL(digest_add_token)(locker, token, (OPAQUE_LEX_YYSTYPE*)yylval); return locker; } @@ -153,12 +153,13 @@ static inline struct PSI_statement_locker * inline_mysql_start_statement(PSI_statement_locker_state *state, PSI_statement_key key, const char *db, uint db_len, + const CHARSET_INFO *charset, const char *src_file, int src_line) { PSI_statement_locker *locker; - locker= PSI_CALL(get_thread_statement_locker)(state, key); + locker= PSI_STATEMENT_CALL(get_thread_statement_locker)(state, key, charset); if (likely(locker != NULL)) - PSI_CALL(start_statement)(locker, db, db_len, src_file, src_line); + PSI_STATEMENT_CALL(start_statement)(locker, db, db_len, src_file, src_line); return locker; } @@ -168,7 +169,7 @@ inline_mysql_refine_statement(PSI_statement_locker *locker, { if (likely(locker != NULL)) { - locker= PSI_CALL(refine_statement)(locker, key); + locker= PSI_STATEMENT_CALL(refine_statement)(locker, key); } return locker; } @@ -179,7 +180,7 @@ inline_mysql_set_statement_text(PSI_statement_locker *locker, { if (likely(locker != NULL)) { - PSI_CALL(set_statement_text)(locker, text, text_len); + PSI_STATEMENT_CALL(set_statement_text)(locker, text, text_len); } } @@ -189,7 +190,7 @@ inline_mysql_set_statement_lock_time(PSI_statement_locker *locker, { if (likely(locker != NULL)) { - PSI_CALL(set_statement_lock_time)(locker, count); + PSI_STATEMENT_CALL(set_statement_lock_time)(locker, count); } } @@ -199,7 +200,7 @@ inline_mysql_set_statement_rows_sent(PSI_statement_locker *locker, { if (likely(locker != NULL)) { - PSI_CALL(set_statement_rows_sent)(locker, count); + PSI_STATEMENT_CALL(set_statement_rows_sent)(locker, count); } } @@ -209,7 +210,7 @@ inline_mysql_set_statement_rows_examined(PSI_statement_locker *locker, { if (likely(locker != NULL)) { - PSI_CALL(set_statement_rows_examined)(locker, count); + PSI_STATEMENT_CALL(set_statement_rows_examined)(locker, count); } } @@ -217,9 +218,9 @@ static inline void inline_mysql_end_statement(struct PSI_statement_locker *locker, Diagnostics_area *stmt_da) { - PSI_CALL(end_stage)(); + PSI_STAGE_CALL(end_stage)(); if (likely(locker != NULL)) - PSI_CALL(end_statement)(locker, stmt_da); + PSI_STATEMENT_CALL(end_statement)(locker, stmt_da); } #endif diff --git a/include/mysql/psi/mysql_table.h b/include/mysql/psi/mysql_table.h index 5067bdaaab8..bd703d75e1f 100644 --- a/include/mysql/psi/mysql_table.h +++ b/include/mysql/psi/mysql_table.h @@ -30,18 +30,20 @@ */ #ifdef HAVE_PSI_TABLE_INTERFACE -#define PSI_CALL_unbind_table PSI_CALL(unbind_table) -#define PSI_CALL_rebind_table PSI_CALL(rebind_table) -#define PSI_CALL_open_table PSI_CALL(open_table) -#define PSI_CALL_close_table PSI_CALL(close_table) -#define PSI_CALL_get_table_share PSI_CALL(get_table_share) -#define PSI_CALL_drop_table_share PSI_CALL(drop_table_share) +#define PSI_CALL_unbind_table PSI_TABLE_CALL(unbind_table) +#define PSI_CALL_rebind_table PSI_TABLE_CALL(rebind_table) +#define PSI_CALL_open_table PSI_TABLE_CALL(open_table) +#define PSI_CALL_close_table PSI_TABLE_CALL(close_table) +#define PSI_CALL_get_table_share PSI_TABLE_CALL(get_table_share) +#define PSI_CALL_release_table_share PSI_TABLE_CALL(release_table_share) +#define PSI_CALL_drop_table_share PSI_TABLE_CALL(drop_table_share) #else #define PSI_CALL_unbind_table(A1) /* no-op */ #define PSI_CALL_rebind_table(A1,A2,A3) NULL #define PSI_CALL_close_table(A1) /* no-op */ #define PSI_CALL_open_table(A1,A2) NULL #define PSI_CALL_get_table_share(A1,A2) NULL +#define PSI_CALL_release_table_share(A1) /* no-op */ #define PSI_CALL_drop_table_share(A1,A2,A3,A4,A5) /* no-op */ #endif @@ -76,22 +78,22 @@ @sa MYSQL_END_TABLE_WAIT. */ #ifdef HAVE_PSI_TABLE_INTERFACE - #define MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD) \ - { \ - if (PSI != NULL) \ - { \ - PSI_table_locker *locker; \ - PSI_table_locker_state state; \ - locker= PSI_CALL(start_table_io_wait)(& state, PSI, OP, INDEX, \ - __FILE__, __LINE__); \ - PAYLOAD \ - if (locker != NULL) \ - PSI_CALL(end_table_io_wait)(locker); \ - } \ - else \ - { \ - PAYLOAD \ - } \ + #define MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD) \ + { \ + if (PSI != NULL) \ + { \ + PSI_table_locker *locker; \ + PSI_table_locker_state state; \ + locker= PSI_TABLE_CALL(start_table_io_wait) \ + (& state, PSI, OP, INDEX, __FILE__, __LINE__); \ + PAYLOAD \ + if (locker != NULL) \ + PSI_TABLE_CALL(end_table_io_wait)(locker); \ + } \ + else \ + { \ + PAYLOAD \ + } \ } #else #define MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD) \ @@ -109,22 +111,22 @@ @sa MYSQL_END_TABLE_WAIT. */ #ifdef HAVE_PSI_TABLE_INTERFACE - #define MYSQL_TABLE_LOCK_WAIT(PSI, OP, FLAGS, PAYLOAD) \ - { \ - if (PSI != NULL) \ - { \ - PSI_table_locker *locker; \ - PSI_table_locker_state state; \ - locker= PSI_CALL(start_table_lock_wait)(& state, PSI, OP, FLAGS, \ - __FILE__, __LINE__); \ - PAYLOAD \ - if (locker != NULL) \ - PSI_CALL(end_table_lock_wait)(locker); \ - } \ - else \ - { \ - PAYLOAD \ - } \ + #define MYSQL_TABLE_LOCK_WAIT(PSI, OP, FLAGS, PAYLOAD) \ + { \ + if (PSI != NULL) \ + { \ + PSI_table_locker *locker; \ + PSI_table_locker_state state; \ + locker= PSI_TABLE_CALL(start_table_lock_wait) \ + (& state, PSI, OP, FLAGS, __FILE__, __LINE__); \ + PAYLOAD \ + if (locker != NULL) \ + PSI_TABLE_CALL(end_table_lock_wait)(locker); \ + } \ + else \ + { \ + PAYLOAD \ + } \ } #else #define MYSQL_TABLE_LOCK_WAIT(PSI, OP, FLAGS, PAYLOAD) \ @@ -180,7 +182,8 @@ inline_mysql_start_table_lock_wait(PSI_table_locker_state *state, if (psi != NULL) { struct PSI_table_locker *locker; - locker= PSI_CALL(start_table_lock_wait)(state, psi, op, flags, src_file, src_line); + locker= PSI_TABLE_CALL(start_table_lock_wait) + (state, psi, op, flags, src_file, src_line); return locker; } return NULL; @@ -194,7 +197,7 @@ static inline void inline_mysql_end_table_lock_wait(struct PSI_table_locker *locker) { if (locker != NULL) - PSI_CALL(end_table_lock_wait)(locker); + PSI_TABLE_CALL(end_table_lock_wait)(locker); } #endif diff --git a/include/mysql/psi/mysql_thread.h b/include/mysql/psi/mysql_thread.h index 78175196fa2..f0d88ff8ede 100644 --- a/include/mysql/psi/mysql_thread.h +++ b/include/mysql/psi/mysql_thread.h @@ -597,7 +597,7 @@ static inline void inline_mysql_mutex_register( ) { #ifdef HAVE_PSI_MUTEX_INTERFACE - PSI_CALL(register_mutex)(category, info, count); + PSI_MUTEX_CALL(register_mutex)(category, info, count); #endif } @@ -613,7 +613,7 @@ static inline int inline_mysql_mutex_init( ) { #ifdef HAVE_PSI_MUTEX_INTERFACE - that->m_psi= PSI_CALL(init_mutex)(key, &that->m_mutex); + that->m_psi= PSI_MUTEX_CALL(init_mutex)(key, &that->m_mutex); #else that->m_psi= NULL; #endif @@ -636,7 +636,7 @@ static inline int inline_mysql_mutex_destroy( #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != NULL) { - PSI_CALL(destroy_mutex)(that->m_psi); + PSI_MUTEX_CALL(destroy_mutex)(that->m_psi); that->m_psi= NULL; } #endif @@ -664,7 +664,7 @@ static inline int inline_mysql_mutex_lock( /* Instrumentation start */ PSI_mutex_locker *locker; PSI_mutex_locker_state state; - locker= PSI_CALL(start_mutex_wait)(&state, that->m_psi, + locker= PSI_MUTEX_CALL(start_mutex_wait)(&state, that->m_psi, PSI_MUTEX_LOCK, src_file, src_line); /* Instrumented code */ @@ -678,7 +678,7 @@ static inline int inline_mysql_mutex_lock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_mutex_wait)(locker, result); + PSI_MUTEX_CALL(end_mutex_wait)(locker, result); return result; } @@ -711,7 +711,7 @@ static inline int inline_mysql_mutex_trylock( /* Instrumentation start */ PSI_mutex_locker *locker; PSI_mutex_locker_state state; - locker= PSI_CALL(start_mutex_wait)(&state, that->m_psi, + locker= PSI_MUTEX_CALL(start_mutex_wait)(&state, that->m_psi, PSI_MUTEX_TRYLOCK, src_file, src_line); /* Instrumented code */ @@ -725,7 +725,7 @@ static inline int inline_mysql_mutex_trylock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_mutex_wait)(locker, result); + PSI_MUTEX_CALL(end_mutex_wait)(locker, result); return result; } @@ -754,7 +754,7 @@ static inline int inline_mysql_mutex_unlock( #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != NULL) - PSI_CALL(unlock_mutex)(that->m_psi); + PSI_MUTEX_CALL(unlock_mutex)(that->m_psi); #endif #ifdef SAFE_MUTEX @@ -781,7 +781,7 @@ static inline void inline_mysql_rwlock_register( ) { #ifdef HAVE_PSI_RWLOCK_INTERFACE - PSI_CALL(register_rwlock)(category, info, count); + PSI_RWLOCK_CALL(register_rwlock)(category, info, count); #endif } @@ -792,7 +792,7 @@ static inline int inline_mysql_rwlock_init( mysql_rwlock_t *that) { #ifdef HAVE_PSI_RWLOCK_INTERFACE - that->m_psi= PSI_CALL(init_rwlock)(key, &that->m_rwlock); + that->m_psi= PSI_RWLOCK_CALL(init_rwlock)(key, &that->m_rwlock); #else that->m_psi= NULL; #endif @@ -810,7 +810,7 @@ static inline int inline_mysql_prlock_init( mysql_prlock_t *that) { #ifdef HAVE_PSI_RWLOCK_INTERFACE - that->m_psi= PSI_CALL(init_rwlock)(key, &that->m_prlock); + that->m_psi= PSI_RWLOCK_CALL(init_rwlock)(key, &that->m_prlock); #else that->m_psi= NULL; #endif @@ -824,7 +824,7 @@ static inline int inline_mysql_rwlock_destroy( #ifdef HAVE_PSI_RWLOCK_INTERFACE if (that->m_psi != NULL) { - PSI_CALL(destroy_rwlock)(that->m_psi); + PSI_RWLOCK_CALL(destroy_rwlock)(that->m_psi); that->m_psi= NULL; } #endif @@ -838,7 +838,7 @@ static inline int inline_mysql_prlock_destroy( #ifdef HAVE_PSI_RWLOCK_INTERFACE if (that->m_psi != NULL) { - PSI_CALL(destroy_rwlock)(that->m_psi); + PSI_RWLOCK_CALL(destroy_rwlock)(that->m_psi); that->m_psi= NULL; } #endif @@ -861,7 +861,7 @@ static inline int inline_mysql_rwlock_rdlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_rdwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)(&state, that->m_psi, PSI_RWLOCK_READLOCK, src_file, src_line); /* Instrumented code */ @@ -869,7 +869,7 @@ static inline int inline_mysql_rwlock_rdlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_rdwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result); return result; } @@ -897,7 +897,7 @@ static inline int inline_mysql_prlock_rdlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_rdwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)(&state, that->m_psi, PSI_RWLOCK_READLOCK, src_file, src_line); /* Instrumented code */ @@ -905,7 +905,7 @@ static inline int inline_mysql_prlock_rdlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_rdwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result); return result; } @@ -933,7 +933,7 @@ static inline int inline_mysql_rwlock_wrlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_wrwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)(&state, that->m_psi, PSI_RWLOCK_WRITELOCK, src_file, src_line); /* Instrumented code */ @@ -941,7 +941,7 @@ static inline int inline_mysql_rwlock_wrlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_wrwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result); return result; } @@ -969,7 +969,7 @@ static inline int inline_mysql_prlock_wrlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_wrwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)(&state, that->m_psi, PSI_RWLOCK_WRITELOCK, src_file, src_line); /* Instrumented code */ @@ -977,7 +977,7 @@ static inline int inline_mysql_prlock_wrlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_wrwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result); return result; } @@ -1005,7 +1005,7 @@ static inline int inline_mysql_rwlock_tryrdlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_rdwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)(&state, that->m_psi, PSI_RWLOCK_TRYREADLOCK, src_file, src_line); /* Instrumented code */ @@ -1013,7 +1013,7 @@ static inline int inline_mysql_rwlock_tryrdlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_rdwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, result); return result; } @@ -1040,7 +1040,7 @@ static inline int inline_mysql_rwlock_trywrlock( /* Instrumentation start */ PSI_rwlock_locker *locker; PSI_rwlock_locker_state state; - locker= PSI_CALL(start_rwlock_wrwait)(&state, that->m_psi, + locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)(&state, that->m_psi, PSI_RWLOCK_TRYWRITELOCK, src_file, src_line); /* Instrumented code */ @@ -1048,7 +1048,7 @@ static inline int inline_mysql_rwlock_trywrlock( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_rwlock_wrwait)(locker, result); + PSI_RWLOCK_CALL(end_rwlock_wrwait)(locker, result); return result; } @@ -1066,7 +1066,7 @@ static inline int inline_mysql_rwlock_unlock( int result; #ifdef HAVE_PSI_RWLOCK_INTERFACE if (that->m_psi != NULL) - PSI_CALL(unlock_rwlock)(that->m_psi); + PSI_RWLOCK_CALL(unlock_rwlock)(that->m_psi); #endif result= rw_unlock(&that->m_rwlock); return result; @@ -1079,7 +1079,7 @@ static inline int inline_mysql_prlock_unlock( int result; #ifdef HAVE_PSI_RWLOCK_INTERFACE if (that->m_psi != NULL) - PSI_CALL(unlock_rwlock)(that->m_psi); + PSI_RWLOCK_CALL(unlock_rwlock)(that->m_psi); #endif result= rw_pr_unlock(&that->m_prlock); return result; @@ -1099,7 +1099,7 @@ static inline void inline_mysql_cond_register( ) { #ifdef HAVE_PSI_COND_INTERFACE - PSI_CALL(register_cond)(category, info, count); + PSI_COND_CALL(register_cond)(category, info, count); #endif } @@ -1111,7 +1111,7 @@ static inline int inline_mysql_cond_init( const pthread_condattr_t *attr) { #ifdef HAVE_PSI_COND_INTERFACE - that->m_psi= PSI_CALL(init_cond)(key, &that->m_cond); + that->m_psi= PSI_COND_CALL(init_cond)(key, &that->m_cond); #else that->m_psi= NULL; #endif @@ -1124,7 +1124,7 @@ static inline int inline_mysql_cond_destroy( #ifdef HAVE_PSI_COND_INTERFACE if (that->m_psi != NULL) { - PSI_CALL(destroy_cond)(that->m_psi); + PSI_COND_CALL(destroy_cond)(that->m_psi); that->m_psi= NULL; } #endif @@ -1147,7 +1147,7 @@ static inline int inline_mysql_cond_wait( /* Instrumentation start */ PSI_cond_locker *locker; PSI_cond_locker_state state; - locker= PSI_CALL(start_cond_wait)(&state, that->m_psi, mutex->m_psi, + locker= PSI_COND_CALL(start_cond_wait)(&state, that->m_psi, mutex->m_psi, PSI_COND_WAIT, src_file, src_line); /* Instrumented code */ @@ -1155,7 +1155,7 @@ static inline int inline_mysql_cond_wait( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_cond_wait)(locker, result); + PSI_COND_CALL(end_cond_wait)(locker, result); return result; } @@ -1184,7 +1184,7 @@ static inline int inline_mysql_cond_timedwait( /* Instrumentation start */ PSI_cond_locker *locker; PSI_cond_locker_state state; - locker= PSI_CALL(start_cond_wait)(&state, that->m_psi, mutex->m_psi, + locker= PSI_COND_CALL(start_cond_wait)(&state, that->m_psi, mutex->m_psi, PSI_COND_TIMEDWAIT, src_file, src_line); /* Instrumented code */ @@ -1192,7 +1192,7 @@ static inline int inline_mysql_cond_timedwait( /* Instrumentation end */ if (locker != NULL) - PSI_CALL(end_cond_wait)(locker, result); + PSI_COND_CALL(end_cond_wait)(locker, result); return result; } @@ -1210,7 +1210,7 @@ static inline int inline_mysql_cond_signal( int result; #ifdef HAVE_PSI_COND_INTERFACE if (that->m_psi != NULL) - PSI_CALL(signal_cond)(that->m_psi); + PSI_COND_CALL(signal_cond)(that->m_psi); #endif result= pthread_cond_signal(&that->m_cond); return result; @@ -1222,7 +1222,7 @@ static inline int inline_mysql_cond_broadcast( int result; #ifdef HAVE_PSI_COND_INTERFACE if (that->m_psi != NULL) - PSI_CALL(broadcast_cond)(that->m_psi); + PSI_COND_CALL(broadcast_cond)(that->m_psi); #endif result= pthread_cond_broadcast(&that->m_cond); return result; @@ -1241,7 +1241,7 @@ static inline void inline_mysql_thread_register( ) { #ifdef HAVE_PSI_THREAD_INTERFACE - PSI_CALL(register_thread)(category, info, count); + PSI_THREAD_CALL(register_thread)(category, info, count); #endif } @@ -1252,14 +1252,14 @@ static inline int inline_mysql_thread_create( void *(*start_routine)(void*), void *arg) { int result; - result= PSI_CALL(spawn_thread)(key, thread, attr, start_routine, arg); + result= PSI_THREAD_CALL(spawn_thread)(key, thread, attr, start_routine, arg); return result; } static inline void inline_mysql_thread_set_psi_id(ulong id) { - struct PSI_thread *psi= PSI_CALL(get_thread)(); - PSI_CALL(set_thread_id)(psi, id); + struct PSI_thread *psi= PSI_THREAD_CALL(get_thread)(); + PSI_THREAD_CALL(set_thread_id)(psi, id); } #endif diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h index 8d5e6db7307..cc2057c630d 100644 --- a/include/mysql/psi/psi.h +++ b/include/mysql/psi/psi.h @@ -899,6 +899,10 @@ struct PSI_file_locker_state_v1 enum PSI_file_operation m_operation; /** Current file. */ struct PSI_file *m_file; + /** Current file name. */ + const char *m_name; + /** Current file class. */ + void *m_class; /** Current thread. */ struct PSI_thread *m_thread; /** Operation number of bytes. */ @@ -958,6 +962,8 @@ struct PSI_digest_storage { my_bool m_full; int m_byte_count; + /** Character set number. */ + uint m_charset_number; unsigned char m_token_array[PSI_MAX_DIGEST_STORAGE_SIZE]; }; typedef struct PSI_digest_storage PSI_digest_storage; @@ -969,6 +975,9 @@ struct PSI_digest_locker_state }; typedef struct PSI_digest_locker_state PSI_digest_locker_state; +/* Duplicate of NAME_LEN, to avoid dependency on mysql_com.h */ +#define PSI_SCHEMA_NAME_LEN (64 * 3) + /** State data storage for @c get_thread_statement_locker_v1_t, @c get_thread_statement_locker_v1_t. @@ -1029,6 +1038,10 @@ struct PSI_statement_locker_state_v1 ulong m_sort_scan; /** Statement digest. */ PSI_digest_locker_state m_digest_state; + /** Current schema name. */ + char m_schema_name[PSI_SCHEMA_NAME_LEN]; + /** Length in bytes of @c m_schema_name. */ + uint m_schema_name_length; }; /** @@ -1187,10 +1200,13 @@ typedef void (*destroy_cond_v1_t)(struct PSI_cond *cond); Socket instrumentation initialisation API. @param key the registered mutex key @param socket descriptor + @param addr the socket ip address + @param addr_len length of socket ip address @return an instrumented socket */ typedef struct PSI_socket* (*init_socket_v1_t) - (PSI_socket_key key, const my_socket *fd); + (PSI_socket_key key, const my_socket *fd, + const struct sockaddr *addr, socklen_t addr_len); /** socket instrumentation destruction API. @@ -1290,7 +1306,7 @@ typedef int (*spawn_thread_v1_t)(PSI_thread_key key, @return an instrumented thread */ typedef struct PSI_thread* (*new_thread_v1_t) - (PSI_thread_key key, const void *identity, ulong thread_id); + (PSI_thread_key key, const void *identity, ulonglong thread_id); /** Assign an id to an instrumented thread. @@ -1298,7 +1314,7 @@ typedef struct PSI_thread* (*new_thread_v1_t) @param id the id to assign */ typedef void (*set_thread_id_v1_t)(struct PSI_thread *thread, - unsigned long id); + ulonglong id); /** Get the instrumentation for the running thread. @@ -1570,16 +1586,18 @@ typedef void (*end_table_lock_wait_v1_t)(struct PSI_table_locker *locker); @param op the operation to perform @param src_file the source file name @param src_line the source line number - @return an instrumented file handle */ -typedef struct PSI_file* (*start_file_open_wait_v1_t) +typedef void (*start_file_open_wait_v1_t) (struct PSI_file_locker *locker, const char *src_file, uint src_line); /** End a file instrumentation open operation, for file streams. @param locker the file locker. + @param result the opened file (NULL indicates failure, non NULL success). + @return an instrumented file handle */ -typedef void (*end_file_open_wait_v1_t)(struct PSI_file_locker *locker); +typedef struct PSI_file* (*end_file_open_wait_v1_t) + (struct PSI_file_locker *locker, void *result); /** End a file instrumentation open operation, for non stream files. @@ -1616,6 +1634,25 @@ typedef void (*start_file_wait_v1_t) typedef void (*end_file_wait_v1_t) (struct PSI_file_locker *locker, size_t count); +/** + Start a file instrumentation close operation. + @param locker the file locker + @param op the operation to perform + @param src_file the source file name + @param src_line the source line number +*/ +typedef void (*start_file_close_wait_v1_t) + (struct PSI_file_locker *locker, const char *src_file, uint src_line); + +/** + End a file instrumentation close operation. + @param locker the file locker. + @param rc the close operation return code (0 for success). + @return an instrumented file handle +*/ +typedef void (*end_file_close_wait_v1_t) + (struct PSI_file_locker *locker, int rc); + /** Start a new stage, and implicitly end the previous stage. @param key the key of the new stage @@ -1632,11 +1669,12 @@ typedef void (*end_stage_v1_t) (void); Get a statement instrumentation locker. @param state data storage for the locker @param key the statement instrumentation key + @param charset client character set @return a statement locker, or NULL */ typedef struct PSI_statement_locker* (*get_thread_statement_locker_v1_t) (struct PSI_statement_locker_state_v1 *state, - PSI_statement_key key); + PSI_statement_key key, const void *charset); /** Refine a statement locker to a more specific key. @@ -1870,6 +1908,19 @@ typedef struct PSI_digest_locker * (*digest_start_v1_t) typedef struct PSI_digest_locker* (*digest_add_token_v1_t) (struct PSI_digest_locker *locker, uint token, struct OPAQUE_LEX_YYSTYPE *yylval); +/** + Stores an array of connection attributes + @param buffer char array of length encoded connection attributes + in network format + @param length legnth of the data in buffer + @param from_cs charset in which @buffer is encodded + @return state + @retval non-0 attributes truncated + @retval 0 stored the attribute +*/ +typedef int (*set_thread_connect_attrs_v1_t)(const char *buffer, uint length, + const void *from_cs); + /** Performance Schema Interface, version 1. @since PSI_VERSION_1 @@ -2005,6 +2056,10 @@ struct PSI_v1 start_file_wait_v1_t start_file_wait; /** @sa end_file_wait_v1_t. */ end_file_wait_v1_t end_file_wait; + /** @sa start_file_close_wait_v1_t. */ + start_file_close_wait_v1_t start_file_close_wait; + /** @sa end_file_close_wait_v1_t. */ + end_file_close_wait_v1_t end_file_close_wait; /** @sa start_stage_v1_t. */ start_stage_v1_t start_stage; /** @sa end_stage_v1_t. */ @@ -2065,6 +2120,8 @@ struct PSI_v1 digest_start_v1_t digest_start; /** @sa digest_add_token_v1_t. */ digest_add_token_v1_t digest_add_token; + /** @sa set_thread_connect_attrs_v1_t. */ + set_thread_connect_attrs_v1_t set_thread_connect_attrs; }; /** @} (end of group Group_PSI_v1) */ @@ -2318,7 +2375,54 @@ typedef struct PSI_stage_info_none PSI_stage_info; extern MYSQL_PLUGIN_IMPORT PSI *PSI_server; -#define PSI_CALL(M) PSI_server->M +/* + Allow to override PSI_XXX_CALL at compile time + with more efficient implementations, if available. + If nothing better is available, + make a dynamic call using the PSI_server function pointer. +*/ + +#ifndef PSI_MUTEX_CALL +#define PSI_MUTEX_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_RWLOCK_CALL +#define PSI_RWLOCK_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_COND_CALL +#define PSI_COND_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_THREAD_CALL +#define PSI_THREAD_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_FILE_CALL +#define PSI_FILE_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_SOCKET_CALL +#define PSI_SOCKET_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_STAGE_CALL +#define PSI_STAGE_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_STATEMENT_CALL +#define PSI_STATEMENT_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_TABLE_CALL +#define PSI_TABLE_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#ifndef PSI_IDLE_CALL +#define PSI_IDLE_CALL(M) PSI_DYNAMIC_CALL(M) +#endif + +#define PSI_DYNAMIC_CALL(M) PSI_server->M /** @} */ diff --git a/include/mysql/psi/psi_abi_v1.h.pp b/include/mysql/psi/psi_abi_v1.h.pp index b0559213998..f2037c5b724 100644 --- a/include/mysql/psi/psi_abi_v1.h.pp +++ b/include/mysql/psi/psi_abi_v1.h.pp @@ -221,6 +221,8 @@ struct PSI_file_locker_state_v1 uint m_flags; enum PSI_file_operation m_operation; struct PSI_file *m_file; + const char *m_name; + void *m_class; struct PSI_thread *m_thread; size_t m_number_of_bytes; ulonglong m_timer_start; @@ -243,6 +245,7 @@ struct PSI_digest_storage { my_bool m_full; int m_byte_count; + uint m_charset_number; unsigned char m_token_array[1024]; }; typedef struct PSI_digest_storage PSI_digest_storage; @@ -278,6 +281,8 @@ struct PSI_statement_locker_state_v1 ulong m_sort_rows; ulong m_sort_scan; PSI_digest_locker_state m_digest_state; + char m_schema_name[(64 * 3)]; + uint m_schema_name_length; }; struct PSI_socket_locker_state_v1 { @@ -318,7 +323,8 @@ typedef struct PSI_cond* (*init_cond_v1_t) (PSI_cond_key key, const void *identity); typedef void (*destroy_cond_v1_t)(struct PSI_cond *cond); typedef struct PSI_socket* (*init_socket_v1_t) - (PSI_socket_key key, const my_socket *fd); + (PSI_socket_key key, const my_socket *fd, + const struct sockaddr *addr, socklen_t addr_len); typedef void (*destroy_socket_v1_t)(struct PSI_socket *socket); typedef struct PSI_table_share* (*get_table_share_v1_t) (my_bool temporary, struct TABLE_SHARE *share); @@ -340,9 +346,9 @@ typedef int (*spawn_thread_v1_t)(PSI_thread_key key, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg); typedef struct PSI_thread* (*new_thread_v1_t) - (PSI_thread_key key, const void *identity, ulong thread_id); + (PSI_thread_key key, const void *identity, ulonglong thread_id); typedef void (*set_thread_id_v1_t)(struct PSI_thread *thread, - unsigned long id); + ulonglong id); typedef struct PSI_thread* (*get_thread_v1_t)(void); typedef void (*set_thread_user_v1_t)(const char *user, int user_len); typedef void (*set_thread_user_host_v1_t)(const char *user, int user_len, @@ -420,9 +426,10 @@ typedef struct PSI_table_locker* (*start_table_lock_wait_v1_t) ulong flags, const char *src_file, uint src_line); typedef void (*end_table_lock_wait_v1_t)(struct PSI_table_locker *locker); -typedef struct PSI_file* (*start_file_open_wait_v1_t) +typedef void (*start_file_open_wait_v1_t) (struct PSI_file_locker *locker, const char *src_file, uint src_line); -typedef void (*end_file_open_wait_v1_t)(struct PSI_file_locker *locker); +typedef struct PSI_file* (*end_file_open_wait_v1_t) + (struct PSI_file_locker *locker, void *result); typedef void (*end_file_open_wait_and_bind_to_descriptor_v1_t) (struct PSI_file_locker *locker, File file); typedef void (*start_file_wait_v1_t) @@ -430,12 +437,16 @@ typedef void (*start_file_wait_v1_t) const char *src_file, uint src_line); typedef void (*end_file_wait_v1_t) (struct PSI_file_locker *locker, size_t count); +typedef void (*start_file_close_wait_v1_t) + (struct PSI_file_locker *locker, const char *src_file, uint src_line); +typedef void (*end_file_close_wait_v1_t) + (struct PSI_file_locker *locker, int rc); typedef void (*start_stage_v1_t) (PSI_stage_key key, const char *src_file, int src_line); typedef void (*end_stage_v1_t) (void); typedef struct PSI_statement_locker* (*get_thread_statement_locker_v1_t) (struct PSI_statement_locker_state_v1 *state, - PSI_statement_key key); + PSI_statement_key key, const void *charset); typedef struct PSI_statement_locker* (*refine_statement_v1_t) (struct PSI_statement_locker *locker, PSI_statement_key key); @@ -499,6 +510,8 @@ typedef struct PSI_digest_locker * (*digest_start_v1_t) (struct PSI_statement_locker *locker); typedef struct PSI_digest_locker* (*digest_add_token_v1_t) (struct PSI_digest_locker *locker, uint token, struct OPAQUE_LEX_YYSTYPE *yylval); +typedef int (*set_thread_connect_attrs_v1_t)(const char *buffer, uint length, + const void *from_cs); struct PSI_v1 { register_mutex_v1_t register_mutex; @@ -566,6 +579,8 @@ struct PSI_v1 end_file_open_wait_and_bind_to_descriptor; start_file_wait_v1_t start_file_wait; end_file_wait_v1_t end_file_wait; + start_file_close_wait_v1_t start_file_close_wait; + end_file_close_wait_v1_t end_file_close_wait; start_stage_v1_t start_stage; end_stage_v1_t end_stage; get_thread_statement_locker_v1_t get_thread_statement_locker; @@ -596,6 +611,7 @@ struct PSI_v1 set_socket_thread_owner_v1_t set_socket_thread_owner; digest_start_v1_t digest_start; digest_add_token_v1_t digest_add_token; + set_thread_connect_attrs_v1_t set_thread_connect_attrs; }; typedef struct PSI_v1 PSI; typedef struct PSI_mutex_info_v1 PSI_mutex_info; diff --git a/include/mysql/service_debug_sync.h b/include/mysql/service_debug_sync.h index bb1202c5e63..eee8e6bbe96 100644 --- a/include/mysql/service_debug_sync.h +++ b/include/mysql/service_debug_sync.h @@ -339,9 +339,16 @@ extern void (*debug_sync_C_callback_ptr)(MYSQL_THD, const char *, size_t); if (debug_sync_service) \ debug_sync_service(thd, STRING_WITH_LEN(name)); \ } while(0) + +#define DEBUG_SYNC_C_IF_THD(thd, name) \ + do { \ + if (debug_sync_service && thd) \ + debug_sync_service((MYSQL_THD) thd, STRING_WITH_LEN(name)); \ + } while(0) #else -#define DEBUG_SYNC(thd,name) do { } while(0) -#endif +#define DEBUG_SYNC(thd,name) do { } while(0) +#define DEBUG_SYNC_C_IF_THD(thd, _sync_point_name_) do { } while(0) +#endif /* defined(ENABLED_DEBUG_SYNC) */ /* compatibility macro */ #define DEBUG_SYNC_C(name) DEBUG_SYNC(NULL, name) diff --git a/include/mysql/service_my_plugin_log.h b/include/mysql/service_my_plugin_log.h new file mode 100644 index 00000000000..0cf7817573c --- /dev/null +++ b/include/mysql/service_my_plugin_log.h @@ -0,0 +1,64 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; version 2 of the + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file + This service provides functions to report error conditions and log to + mysql error log. +*/ + +#ifndef MYSQL_SERVICE_MY_PLUGIN_LOG_INCLUDED +#define MYSQL_SERVICE_MY_PLUGIN_LOG_INCLUDED + +#ifndef MYSQL_ABI_CHECK +#include +#endif + +/* keep in sync with the loglevel enum in my_sys.h */ +enum plugin_log_level +{ + MY_ERROR_LEVEL, + MY_WARNING_LEVEL, + MY_INFORMATION_LEVEL +}; + + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct my_plugin_log_service +{ + /** write a message to the log */ + int (*my_plugin_log_message)(MYSQL_PLUGIN *, enum plugin_log_level, const char *, ...); +} *my_plugin_log_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define my_plugin_log_message my_plugin_log_service->my_plugin_log_message + +#else + +int my_plugin_log_message(MYSQL_PLUGIN *plugin, enum plugin_log_level level, + const char *format, ...); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/mysql/service_sha1.h b/include/mysql/service_sha1.h new file mode 100644 index 00000000000..01f5ba81566 --- /dev/null +++ b/include/mysql/service_sha1.h @@ -0,0 +1,57 @@ +#ifndef MYSQL_SERVICE_SHA1_INCLUDED +/* Copyright (c) 2013, Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/** + @file + my sha1 service + + Functions to calculate SHA1 hash from a memory buffer +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef MYSQL_ABI_CHECK +#include +#endif + +#define MY_SHA1_HASH_SIZE 20 /* Hash size in bytes */ + +extern struct my_sha1_service_st { + void (*my_sha1_type)(unsigned char*, const char*, size_t); + void (*my_sha1_multi_type)(unsigned char*, ...); +} *my_sha1_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN + +#define my_sha1(A,B,C) my_sha1_service->my_sha1_type(A,B,C) +#define my_sha1_multi my_sha1_service->my_sha1_multi_type + +#else + +void my_sha1(unsigned char*, const char*, size_t); +void my_sha1_multi(unsigned char*, ...); + +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_SHA1_INCLUDED +#endif + diff --git a/include/mysql/services.h b/include/mysql/services.h index 1145d19872b..49670b5673b 100644 --- a/include/mysql/services.h +++ b/include/mysql/services.h @@ -26,6 +26,7 @@ extern "C" { #include #include #include +#include #ifdef __cplusplus } diff --git a/include/mysql_com.h b/include/mysql_com.h index a9bba2f59a3..cc206a0230f 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -68,6 +68,7 @@ #define TABLE_COMMENT_MAXLEN 2048 #define COLUMN_COMMENT_MAXLEN 1024 #define INDEX_COMMENT_MAXLEN 1024 +#define TABLE_PARTITION_COMMENT_MAXLEN 1024 /* USER_HOST_BUFF_SIZE -- length of string buffer, that is enough to contain @@ -142,13 +143,19 @@ enum enum_server_command #define BINCMP_FLAG 131072 /* Intern: Used by sql_yacc */ #define GET_FIXED_FIELDS_FLAG (1 << 18) /* Used to get fields in item tree */ #define FIELD_IN_PART_FUNC_FLAG (1 << 19)/* Field part of partition func */ -#define FIELD_IN_ADD_INDEX (1<< 20) /* Intern: Field used in ADD INDEX */ + +/** + Intern: Field in TABLE object for new version of altered table, + which participates in a newly added index. +*/ +#define FIELD_IN_ADD_INDEX (1 << 20) #define FIELD_IS_RENAMED (1<< 21) /* Intern: Field is being renamed */ -#define FIELD_FLAGS_STORAGE_MEDIA 22 /* Field storage media, bit 22-23, - reserved by MySQL Cluster */ -#define FIELD_FLAGS_COLUMN_FORMAT 24 /* Field column format, bit 24-25, - reserved by MySQL Cluster */ -#define HAS_EXPLICIT_VALUE (1 << 26) /* An INSERT/UPDATE operation supplied +#define FIELD_FLAGS_STORAGE_MEDIA 22 /* Field storage media, bit 22-23 */ +#define FIELD_FLAGS_STORAGE_MEDIA_MASK (3 << FIELD_FLAGS_STORAGE_MEDIA) +#define FIELD_FLAGS_COLUMN_FORMAT 24 /* Field column format, bit 24-25 */ +#define FIELD_FLAGS_COLUMN_FORMAT_MASK (3 << FIELD_FLAGS_COLUMN_FORMAT) +#define FIELD_IS_DROPPED (1<< 26) /* Intern: Field is being dropped */ +#define HAS_EXPLICIT_VALUE (1 << 27) /* An INSERT/UPDATE operation supplied an explicit default value */ #define REFRESH_GRANT (1ULL << 0) /* Refresh grant tables */ @@ -177,11 +184,12 @@ enum enum_server_command #define REFRESH_QUERY_CACHE_FREE (1ULL << 17) /* pack query cache */ #define REFRESH_DES_KEY_FILE (1ULL << 18) #define REFRESH_USER_RESOURCES (1ULL << 19) +#define REFRESH_FOR_EXPORT (1ULL << 20) /* FLUSH TABLES ... FOR EXPORT */ -#define REFRESH_TABLE_STATS (1ULL << 20) /* Refresh table stats hash table */ -#define REFRESH_INDEX_STATS (1ULL << 21) /* Refresh index stats hash table */ -#define REFRESH_USER_STATS (1ULL << 22) /* Refresh user stats hash table */ -#define REFRESH_CLIENT_STATS (1ULL << 23) /* Refresh client stats hash table */ +#define REFRESH_TABLE_STATS (1ULL << 27) /* Refresh table stats hash table */ +#define REFRESH_INDEX_STATS (1ULL << 28) /* Refresh index stats hash table */ +#define REFRESH_USER_STATS (1ULL << 29) /* Refresh user stats hash table */ +#define REFRESH_CLIENT_STATS (1ULL << 30) /* Refresh client stats hash table */ #define REFRESH_FAST (1ULL << 31) /* Intern flag */ @@ -206,8 +214,15 @@ enum enum_server_command #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */ #define CLIENT_PLUGIN_AUTH (1UL << 19) /* Client supports plugin authentication */ -#define CLIENT_PROGRESS (1UL << 29) /* Client support progress indicator */ +#define CLIENT_PLUGIN_AUTH (1UL << 19) /* Client supports plugin authentication */ +#define CLIENT_CONNECT_ATTRS (1UL << 20) /* Client supports connection attributes */ +/* Enable authentication response packet to be larger than 255 bytes. */ +#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA (1UL << 21) +/* Don't close the connection for a connection with expired password. */ +#define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) + +#define CLIENT_PROGRESS (1UL << 29) /* Client support progress indicator */ #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) /* It used to be that if mysql_real_connect() failed, it would delete any @@ -251,6 +266,12 @@ enum enum_server_command CLIENT_PROGRESS | \ CLIENT_PLUGIN_AUTH) +/* + To be added later: + CLIENT_CONNECT_ATTRS, CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA, + CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS +*/ + /* Switch off the flags that are optional and depending on build flags If any of the optional flags is supported by the build it will be switched diff --git a/include/password.h b/include/password.h index 082f917e7c0..5dfea533546 100644 --- a/include/password.h +++ b/include/password.h @@ -24,6 +24,8 @@ void my_make_scrambled_password_323(char *to, const char *password, size_t pass_len); void my_make_scrambled_password(char *to, const char *password, size_t pass_len); +void my_make_scrambled_password_sha1(char *to, const char *password, + size_t pass_len); void hash_password(ulong *result, const char *password, uint password_len); diff --git a/include/service_versions.h b/include/service_versions.h index 2dffa7cf863..b2c5ccd1948 100644 --- a/include/service_versions.h +++ b/include/service_versions.h @@ -20,10 +20,13 @@ #define SERVICE_VERSION void * #endif +#define VERSION_debug_sync 0x1000 +#define VERSION_kill_statement 0x1000 + #define VERSION_my_snprintf 0x0100 #define VERSION_thd_alloc 0x0100 #define VERSION_thd_wait 0x0100 #define VERSION_progress_report 0x0100 -#define VERSION_debug_sync 0x1000 -#define VERSION_kill_statement 0x1000 #define VERSION_thd_timezone 0x0100 +#define VERSION_my_sha1 0x0100 + diff --git a/include/sha1.h b/include/sha1.h index c3469333c27..d927cd26ad9 100644 --- a/include/sha1.h +++ b/include/sha1.h @@ -1,8 +1,7 @@ #ifndef SHA1_INCLUDED #define SHA1_INCLUDED -/* Copyright (c) 2002, 2006 MySQL AB, 2009 Sun Microsystems, Inc. - Use is subject to license terms. +/* Copyright (c) 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,88 +17,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/* - This is the header file for code which implements the Secure - Hashing Algorithm 1 as defined in FIPS PUB 180-1 published - April 17, 1995. - - Many of the variable names in this code, especially the - single character names, were used because those were the names - used in the publication. - - Please read the file sha1.c for more information. - - Modified 2002 by Peter Zaitsev to better follow MySQL standards - - Original Source from: http://www.faqs.org/rfcs/rfc3174.html - - Copyright (C) The Internet Society (2001). All Rights Reserved. - - This document and translations of it may be copied and furnished to - others, and derivative works that comment on or otherwise explain it - or assist in its implementation may be prepared, copied, published - and distributed, in whole or in part, without restriction of any - kind, provided that the above copyright notice and this paragraph are - included on all such copies and derivative works. However, this - document itself may not be modified in any way, such as by removing - the copyright notice or references to the Internet Society or other - Internet organizations, except as needed for the purpose of - developing Internet standards in which case the procedures for - copyrights defined in the Internet Standards process must be - followed, or as required to translate it into languages other than - English. - - The limited permissions granted above are perpetual and will not be - revoked by the Internet Society or its successors or assigns. - - This document and the information contained herein is provided on an - "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING - TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING - BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION - HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF - MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - - Acknowledgement - Funding for the RFC Editor function is currently provided by the - Internet Society. -*/ - - -enum sha_result_codes -{ - SHA_SUCCESS = 0, - SHA_NULL, /* Null pointer parameter */ - SHA_INPUT_TOO_LONG, /* input data too long */ - SHA_STATE_ERROR /* called Input after Result */ -}; - -#define SHA1_HASH_SIZE 20 /* Hash size in bytes */ - -/* - This structure will hold context information for the SHA-1 - hashing operation -*/ - -typedef struct SHA1_CONTEXT -{ - ulonglong Length; /* Message length in bits */ - uint32 Intermediate_Hash[SHA1_HASH_SIZE/4]; /* Message Digest */ - int Computed; /* Is the digest computed? */ - int Corrupted; /* Is the message digest corrupted? */ - int16 Message_Block_Index; /* Index into message block array */ - uint8 Message_Block[64]; /* 512-bit message blocks */ -} SHA1_CONTEXT; - -/* - Function Prototypes -*/ - -C_MODE_START - -int mysql_sha1_reset(SHA1_CONTEXT*); -int mysql_sha1_input(SHA1_CONTEXT*, const uint8 *, unsigned int); -int mysql_sha1_result(SHA1_CONTEXT* , uint8 Message_Digest[SHA1_HASH_SIZE]); - -C_MODE_END +#include +#define SHA1_HASH_SIZE MY_SHA1_HASH_SIZE +#define compute_sha1_hash(A,B,C) my_sha1(A,B,C) +#define compute_sha1_hash_multi(A,B,C,D,E) my_sha1_multi(A,B,C,D,E,NULL) #endif /* SHA__INCLUDED */ diff --git a/include/sql_common.h b/include/sql_common.h index 406a87010fb..5a033fbe522 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -21,6 +21,7 @@ extern "C" { #endif #include +#include extern const char *unknown_sqlstate; extern const char *cant_connect_sqlstate; @@ -34,6 +35,7 @@ struct st_mysql_options_extention { char *default_auth; char *ssl_crl; /* PEM CRL file */ char *ssl_crlpath; /* PEM directory of CRL-s? */ + char *server_public_key_path; void (*report_progress)(const MYSQL *mysql, unsigned int stage, unsigned int max_stage, diff --git a/include/thread_pool_priv.h b/include/thread_pool_priv.h index 9a9c65af6da..449c8ded66b 100644 --- a/include/thread_pool_priv.h +++ b/include/thread_pool_priv.h @@ -49,7 +49,6 @@ void thd_set_killed(THD *thd); void thd_clear_errors(THD *thd); void thd_set_thread_stack(THD *thd, char *stack_start); void thd_lock_thread_count(THD *thd); -void thd_unlock_thread_count(THD *thd); void thd_close_connection(THD *thd); THD *thd_get_current_thd(); void thd_lock_data(THD *thd); diff --git a/libevent/CMakeLists.txt b/libevent/CMakeLists.txt new file mode 100644 index 00000000000..ea50bab2530 --- /dev/null +++ b/libevent/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +# Common defines and includes +IF(WITH_INNODB_MEMCACHED AND UNIX) + +ADD_DEFINITIONS(-DHAVE_CONFIG_H) +INCLUDE_DIRECTORIES(${LIBEVENT_INCLUDE_DIR}/compat/sys + ${LIBEVENT_INCLUDE_DIR}) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_SHARED_LIBRARY_C_FLAGS} -I${LIBEVENT_INCLUDE_DIR}") + +SET(LIBEVENT_CORE_SOURCES + event.h + event-internal.h + evutil.h + log.h + event.c + buffer.c + evbuffer.c + log.c + evutil.c) + +SET(LIBEVENT_EXTRA_SOURCES + event_tagging.c + http.c + evhttp.h + http-internal.h + evdns.h + evrpc.c + evrpc.h + evrpc-internal.h + strlcpy.c + strlcpy-internal.h) + +IF(HAVE_SIGNAL_H) + SET(LIBEVENT_SIGNAL_SOURCES signal.c) +ENDIF() + +IF(HAVE_POLL_H) + SET(LIBEVENT_POLL_SOURCES poll.c) +ENDIF() + +IF(HAVE_SELECT) + SET(LIBEVENT_SELECT_SOURCE select.c) +ENDIF() + +IF(HAVE_SYS_EPOLL_H) + SET(LIBEVENT_EPOLL_SOURCES epoll.c epoll_sub.c) +ENDIF() + +IF(HAVE_SYS_DEVPOLL_H) + SET(LIBEVENT_DEVPOLL_SOURCES devpoll.c) +ENDIF() + +IF(HAVE_EVENT_PORTS) + SET(LIBEVENT_EVPORT_SOURCES evport.c) +ENDIF() + +IF(HAVE_WORKING_KQUEUE) + SET(LIBEVENT_KQUEUE_SOURCES kqueue.c) +ENDIF() + +ADD_LIBRARY(event_share SHARED ${LIBEVENT_CORE_SOURCES} ${LIBEVENT_EXTRA_SOURCES} ${LIBEVENT_SIGNAL_SOURCES} ${LIBEVENT_POLL_SOURCES} ${LIBEVENT_SELECT_SOURCE} ${LIBEVENT_EPOLL_SOURCES} ${LIBEVENT_DEVPOLL_SOURCES} ${LIBEVENT_EVPORT_SOURCES} ${LIBEVENT_KQUEUE_SOURCES}) + +ADD_LIBRARY(event STATIC ${LIBEVENT_CORE_SOURCES} ${LIBEVENT_EXTRA_SOURCES} ${LIBEVENT_SIGNAL_SOURCES} ${LIBEVENT_POLL_SOURCES} ${LIBEVENT_SELECT_SOURCE} ${LIBEVENT_EPOLL_SOURCES} ${LIBEVENT_DEVPOLL_SOURCES} ${LIBEVENT_EVPORT_SOURCES} ${LIBEVENT_KQUEUE_SOURCES}) +ENDIF() + diff --git a/libevent/ChangeLog b/libevent/ChangeLog new file mode 100644 index 00000000000..2435c1f15d8 --- /dev/null +++ b/libevent/ChangeLog @@ -0,0 +1,190 @@ +Changes in 1.4.12-stable: + o Try to contain degree of failure when running on a win32 version so heavily firewalled that we can't fake a socketpair. + o Fix an obscure timing-dependent, allocator-dependent crash in the evdns code. + o Use __VA_ARGS__ syntax for varargs macros in event_rpcgen when compiler is not GCC. + o Activate fd events in a pseudorandom order with O(N) backends, so that we don't systematically favor low fds (select) or earlier-added fds (poll, win32). + o Fix another pair of fencepost bugs in epoll.c. [Patch from Adam Langley.] + o Do not break evdns connections to nameservers when our IP changes. + o Set truncated flag correctly in evdns server replies. + o Disable strict aliasing with GCC: our code is not compliant with it. + +Changes in 1.4.11-stable: + o Fix a bug when removing a timeout from the heap. [Patch from Marko Kreen] + o Remove the limit on size of HTTP headers by removing static buffers. + o Fix a nasty dangling pointer bug in epoll.c that could occur after epoll_recalc(). [Patch from Kevin Springborn] + o Distribute Win32-Code/event-config.h, not ./event-config.h + +Changes in 1.4.10-stable: + o clean up buffered http connection data on reset; reported by Brian O'Kelley + o bug fix and potential race condition in signal handling; from Alexander Drozdov + o rename the Solaris event ports backend to evport + o support compilation on Haiku + o fix signal processing when a signal callback delivers a signal; from Alexander Drozdov + o const-ify some arguments to evdns functions. + o off-by-one error in epoll_recalc; reported by Victor Goya + o include Doxyfile in tar ball; from Jeff Garzik + o correctly parse queries with encoded \r, \n or + characters + +Changes in 1.4.9-stable: + o event_add would not return error for some backends; from Dean McNamee + o Clear the timer cache on entering the event loop; reported by Victor Chang + o Only bind the socket on connect when a local address has been provided; reported by Alejo Sanchez + o Allow setting of local port for evhttp connections to support millions of connections from a single system; from Richard Jones. + o Clear the timer cache when leaving the event loop; reported by Robin Haberkorn + o Fix a typo in setting the global event base; reported by lance. + o Fix a memory leak when reading multi-line headers + o Fix a memory leak by not running explicit close detection for server connections + +Changes in 1.4.8-stable: + o Match the query in DNS replies to the query in the request; from Vsevolod Stakhov. + o Fix a merge problem in which name_from_addr returned pointers to the stack; found by Jiang Hong. + o Do not remove Accept-Encoding header + +Changes in 1.4.7-stable: + o Fix a bug where headers arriving in multiple packets were not parsed; fix from Jiang Hong; test by me. + +Changes in 1.4.6-stable: + o evutil.h now includes directly + o switch all uses of [v]snprintf over to evutil + o Correct handling of trailing headers in chunked replies; from Scott Lamb. + o Support multi-line HTTP headers; based on a patch from Moshe Litvin + o Reject negative Content-Length headers; anonymous bug report + o Detect CLOCK_MONOTONIC at runtime for evdns; anonymous bug report + o Fix a bug where deleting signals with the kqueue backend would cause subsequent adds to fail + o Support multiple events listening on the same signal; make signals regular events that go on the same event queue; problem report by Alexander Drozdov. + o Deal with evbuffer_read() returning -1 on EINTR|EAGAIN; from Adam Langley. + o Fix a bug in which the DNS server would incorrectly set the type of a cname reply to a. + o Fix a bug where setting the timeout on a bufferevent would take not effect if the event was already pending. + o Fix a memory leak when using signals for some event bases; reported by Alexander Drozdov. + o Add libevent.vcproj file to distribution to help with Windows build. + o Fix a problem with epoll() and reinit; problem report by Alexander Drozdov. + o Fix off-by-one errors in devpoll; from Ian Bell + o Make event_add not change any state if it fails; reported by Ian Bell. + o Do not warn on accept when errno is either EAGAIN or EINTR + +Changes in 1.4.5-stable: + o Fix connection keep-alive behavior for HTTP/1.0 + o Fix use of freed memory in event_reinit; pointed out by Peter Postma + o Constify struct timeval * where possible; pointed out by Forest Wilkinson + o allow min_heap_erase to be called on removed members; from liusifan. + o Rename INPUT and OUTPUT to EVRPC_INPUT and EVRPC_OUTPUT. Retain INPUT/OUTPUT aliases on on-win32 platforms for backwards compatibility. + o Do not use SO_REUSEADDR when connecting + o Fix Windows build + o Fix a bug in event_rpcgen when generated fixed-sized entries + +Changes in 1.4.4-stable: + o Correct the documentation on buffer printf functions. + o Don't warn on unimplemented epoll_create(): this isn't a problem, just a reason to fall back to poll or select. + o Correctly handle timeouts larger than 35 minutes on Linux with epoll.c. This is probably a kernel defect, but we'll have to support old kernels anyway even if it gets fixed. + o Fix a potential stack corruption bug in tagging on 64-bit CPUs. + o expose bufferevent_setwatermark via header files and fix high watermark on read + o fix a bug in bufferevent read water marks and add a test for them + o introduce bufferevent_setcb and bufferevent_setfd to allow better manipulation of bufferevents + o use libevent's internal timercmp on all platforms, to avoid bugs on old platforms where timercmp(a,b,<=) is buggy. + o reduce system calls for getting current time by caching it. + o fix evhttp_bind_socket() so that multiple sockets can be bound by the same http server. + o Build test directory correctly with CPPFLAGS set. + o Fix build under Visual C++ 2005. + o Expose evhttp_accept_socket() API. + o Merge windows gettimeofday() replacement into a new evutil_gettimeofday() function. + o Fix autoconf script behavior on IRIX. + o Make sure winsock2.h include always comes before windows.h include. + +Changes in 1.4.3-stable: + o include Content-Length in reply for HTTP/1.0 requests with keep-alive + o Patch from Tani Hosokawa: make some functions in http.c threadsafe. + o Do not free the kqop file descriptor in other processes, also allow it to be 0; from Andrei Nigmatulin + o make event_rpcgen.py generate code include event-config.h; reported by Sam Banks. + o make event methods static so that they are not exported; from Andrei Nigmatulin + o make RPC replies use application/octet-stream as mime type + o do not delete uninitialized timeout event in evdns + +Changes in 1.4.2-rc: + o remove pending timeouts on event_base_free() + o also check EAGAIN for Solaris' event ports; from W.C.A. Wijngaards + o devpoll and evport need reinit; tested by W.C.A Wijngaards + o event_base_get_method; from Springande Ulv + o Send CRLF after each chunk in HTTP output, for compliance with RFC2626. Patch from "propanbutan". Fixes bug 1894184. + o Add a int64_t parsing function, with unit tests, so we can apply Scott Lamb's fix to allow large HTTP values. + o Use a 64-bit field to hold HTTP content-lengths. Patch from Scott Lamb. + o Allow regression code to build even without Python installed + o remove NDEBUG ifdefs from evdns.c + o update documentation of event_loop and event_base_loop; from Tani Hosokawa. + o detect integer types properly on platforms without stdint.h + o Remove "AM_MAINTAINER_MODE" declaration in configure.in: now makefiles and configure should get re-generated automatically when Makefile.am or configure.in chanes. + o do not insert event into list when evsel->add fails + +Changes in 1.4.1-beta: + o free minheap on event_base_free(); from Christopher Layne + o debug cleanups in signal.c; from Christopher Layne + o provide event_base_new() that does not set the current_base global + o bufferevent_write now uses a const source argument; report from Charles Kerr + o better documentation for event_base_loopexit; from Scott Lamb. + o Make kqueue have the same behavior as other backends when a signal is caught between event_add() and event_loop(). Previously, it would catch and ignore such signals. + o Make kqueue restore signal handlers correctly when event_del() is called. + o provide event_reinit() to reintialize an event_base after fork + o small improvements to evhttp documentation + o always generate Date and Content-Length headers for HTTP/1.1 replies + o set the correct event base for HTTP close events + o New function, event_{base_}loopbreak. Like event_loopexit, it makes an event loop stop executing and return. Unlike event_loopexit, it keeps subsequent pending events from getting executed. Patch from Scott Lamb + o Removed obsoleted recalc code + o pull setters/getters out of RPC structures into a base class to which we just need to store a pointer; this reduces the memory footprint of these structures. + o fix a bug with event_rpcgen for integers + o move EV_PERSIST handling out of the event backends + o support for 32-bit tag numbers in rpc structures; this is wire compatible, but changes the API slightly. + o prefix {encode,decode}_tag functions with evtag to avoid collisions + o Correctly handle DNS replies with no answers set (Fixes bug 1846282) + o The configure script now takes an --enable-gcc-warnigns option that turns on many optional gcc warnings. (Nick has been building with these for a while, but they might be useful to other developers.) + o When building with GCC, use the "format" attribute to verify type correctness of calls to printf-like functions. + o removed linger from http server socket; reported by Ilya Martynov + o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. + o demote most http warnings to debug messages + o Fix Solaris compilation; from Magne Mahre + o Add a "Date" header to HTTP responses, as required by HTTP 1.1. + o Support specifying the local address of an evhttp_connection using set_local_address + o Fix a memory leak in which failed HTTP connections would not free the request object + o Make adding of array members in event_rpcgen more efficient, but doubling memory allocation + o Fix a memory leak in the DNS server + o Fix compilation when DNS_USE_OPENSSL_FOR_ID is enabled + o Fix buffer size and string generation in evdns_resolve_reverse_ipv6(). + o Respond to nonstandard DNS queries with "NOTIMPL" rather than by ignoring them. + o In DNS responses, the CD flag should be preserved, not the TC flag. + o Fix http.c to compile properly with USE_DEBUG; from Christopher Layne + o Handle NULL timeouts correctly on Solaris; from Trond Norbye + o Recalculate pending events properly when reallocating event array on Solaris; from Trond Norbye + o Add Doxygen documentation to header files; from Mark Heily + o Add a evdns_set_transaction_id_fn() function to override the default + transaction ID generation code. + o Add an evutil module (with header evutil.h) to implement our standard cross-platform hacks, on the theory that somebody else would like to use them too. + o Fix signals implementation on windows. + o Fix http module on windows to close sockets properly. + o Make autogen.sh script run correctly on systems where /bin/sh isn't bash. (Patch from Trond Norbye, rewritten by Hagne Mahre and then Hannah Schroeter.) + o Skip calling gettime() in timeout_process if we are not in fact waiting for any events. (Patch from Trond Norbye) + o Make test subdirectory compile under mingw. + o Fix win32 buffer.c behavior so that it is correct for sockets (which do not like ReadFile and WriteFile). + o Make the test.sh script run unit tests for the evpoll method. + o Make the entire evdns.h header enclosed in "extern C" as appropriate. + o Fix implementation of strsep on platforms that lack it + o Fix implementation of getaddrinfo on platforms that lack it; mainly, this will make Windows http.c work better. Original patch by Lubomir Marinov. + o Fix evport implementation: port_disassociate called on unassociated events resulting in bogus errors; more efficient memory management; from Trond Norbye and Prakash Sangappa + o support for hooks on rpc input and output; can be used to implement rpc independent processing such as compression or authentication. + o use a min heap instead of a red-black tree for timeouts; as a result finding the min is a O(1) operation now; from Maxim Yegorushkin + o associate an event base with an rpc pool + o added two additional libraries: libevent_core and libevent_extra in addition to the regular libevent. libevent_core contains only the event core whereas libevent_extra contains dns, http and rpc support + o Begin using libtool's library versioning support correctly. If we don't mess up, this will more or less guarantee binaries linked against old versions of libevent continue working when we make changes to libevent that do not break backward compatibility. + o Fix evhttp.h compilation when TAILQ_ENTRY is not defined. + o Small code cleanups in epoll_dispatch(). + o Increase the maximum number of addresses read from a packet in evdns to 32. + o Remove support for the rtsig method: it hasn't compiled for a while, and nobody seems to miss it very much. Let us know if there's a good reason to put it back in. + o Rename the "class" field in evdns_server_request to dns_question_class, so that it won't break compilation under C++. Use a macro so that old code won't break. Mark the macro as deprecated. + o Fix DNS unit tests so that having a DNS server with broken IPv6 support is no longer cause for aborting the unit tests. + o Make event_base_free() succeed even if there are pending non-internal events on a base. This may still leak memory and fds, but at least it no longer crashes. + o Post-process the config.h file into a new, installed event-config.h file that we can install, and whose macros will be safe to include in header files. + o Remove the long-deprecated acconfig.h file. + o Do not require #include before #include . + o Add new evutil_timer* functions to wrap (or replace) the regular timeval manipulation functions. + o Fix many build issues when using the Microsoft C compiler. + o Remove a bash-ism in autogen.sh + o When calling event_del on a signal, restore the signal handler's previous value rather than setting it to SIG_DFL. Patch from Christopher Layne. + o Make the logic for active events work better with internal events; patch from Christopher Layne. + o We do not need to specially remove a timeout before calling event_del; patch from Christopher Layne. diff --git a/libevent/Doxyfile b/libevent/Doxyfile new file mode 100644 index 00000000000..77f6de89b46 --- /dev/null +++ b/libevent/Doxyfile @@ -0,0 +1,230 @@ +# Doxyfile 1.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libevent + +# Place all output under 'doxygen/' + +OUTPUT_DIRECTORY = doxygen/ + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = event.h evdns.h evhttp.h evrpc.h + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = TAILQ_ENTRY RB_ENTRY _EVENT_DEFINED_TQENTRY + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES diff --git a/libevent/Makefile.am b/libevent/Makefile.am new file mode 100644 index 00000000000..8d9d7520373 --- /dev/null +++ b/libevent/Makefile.am @@ -0,0 +1,124 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +# This is the point release for libevent. It shouldn't include any +# a/b/c/d/e notations. +RELEASE = 1.4 + +# This is the version info for the libevent binary API. It has three +# numbers: +# Current -- the number of the binary API that we're implementing +# Revision -- which iteration of the implementation of the binary +# API are we supplying? +# Age -- How many previous binary API versions do we also +# support? +# +# If we release a new version that does not change the binary API, +# increment Revision. +# +# If we release a new version that changes the binary API, but does +# not break programs compiled against the old binary API, increment +# Current and Age. Set Revision to 0, since this is the first +# implementation of the new API. +# +# Otherwise, we're changing the binary API and breaking bakward +# compatibility with old binaries. Increment Current. Set Age to 0, +# since we're backward compatible with no previous APIs. Set Revision +# to 0 too. + +# History: +# Libevent 1.4.1 was 2:0:0 +# Libevent 1.4.2 should be 3:0:0 +# Libevent 1.4.5 is 3:0:1 (we forgot to increment in the past) +VERSION_INFO = 3:3:1 + +bin_SCRIPTS = event_rpcgen.py + +EXTRA_DIST = autogen.sh event.h event-internal.h log.h evsignal.h evdns.3 \ + evrpc.h evrpc-internal.h min_heap.h \ + event.3 \ + Doxyfile \ + kqueue.c epoll_sub.c epoll.c select.c poll.c signal.c \ + evport.c devpoll.c event_rpcgen.py \ + sample/Makefile.am sample/Makefile.in sample/event-test.c \ + sample/signal-test.c sample/time-test.c \ + test/Makefile.am test/Makefile.in test/bench.c test/regress.c \ + test/test-eof.c test/test-weof.c test/test-time.c \ + test/test-init.c test/test.sh \ + compat/sys/queue.h compat/sys/_time.h \ + WIN32-Code/config.h \ + WIN32-Code/event-config.h \ + WIN32-Code/win32.c \ + WIN32-Code/tree.h \ + WIN32-Prj/event_test/event_test.dsp \ + WIN32-Prj/event_test/test.txt WIN32-Prj/libevent.dsp \ + WIN32-Prj/libevent.dsw WIN32-Prj/signal_test/signal_test.dsp \ + WIN32-Prj/time_test/time_test.dsp WIN32-Prj/regress/regress.vcproj \ + WIN32-Prj/libevent.sln WIN32-Prj/libevent.vcproj + +lib_LTLIBRARIES = libevent.la libevent_core.la libevent_extra.la + +if BUILD_WIN32 + +SUBDIRS = . sample +SYS_LIBS = -lws2_32 +SYS_SRC = WIN32-Code/win32.c +SYS_INCLUDES = -IWIN32-Code + +else + +SUBDIRS = . sample test +SYS_LIBS = +SYS_SRC = +SYS_INCLUDES = + +endif + +BUILT_SOURCES = event-config.h + +event-config.h: 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_/' < config.h >> $@ + echo "#endif" >> $@ + +CORE_SRC = event.c buffer.c evbuffer.c log.c evutil.c $(SYS_SRC) +EXTRA_SRC = event_tagging.c http.c evhttp.h http-internal.h evdns.c \ + evdns.h evrpc.c evrpc.h evrpc-internal.h \ + strlcpy.c strlcpy-internal.h strlcpy-internal.h + +libevent_la_SOURCES = $(CORE_SRC) $(EXTRA_SRC) +libevent_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO) + +libevent_core_la_SOURCES = $(CORE_SRC) +libevent_core_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_core_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO) + +libevent_extra_la_SOURCES = $(EXTRA_SRC) +libevent_extra_la_LIBADD = @LTLIBOBJS@ $(SYS_LIBS) +libevent_extra_la_LDFLAGS = -release $(RELEASE) -version-info $(VERSION_INFO) + +include_HEADERS = event.h evhttp.h evdns.h evrpc.h evutil.h + +nodist_include_HEADERS = event-config.h + +INCLUDES = -I$(srcdir)/compat $(SYS_INCLUDES) + +man_MANS = event.3 evdns.3 + +verify: libevent.la + cd test && make verify + +doxygen: FORCE + doxygen $(srcdir)/Doxyfile +FORCE: + +DISTCLEANFILES = *~ event-config.h diff --git a/libevent/README b/libevent/README new file mode 100644 index 00000000000..b0650392ed4 --- /dev/null +++ b/libevent/README @@ -0,0 +1,57 @@ +To build libevent, type + +$ ./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 + +You can run the regression tests by + +$ make verify + +Before, reporting any problems, please run the regression tests. + +To enable the low-level tracing build the library as: + +CFLAGS=-DUSE_DEBUG ./configure [...] + +Acknowledgements: +----------------- + +The following people have helped with suggestions, ideas, code or +fixing bugs: + + Alejo + Weston Andros Adamson + William Ahern + Stas Bekman + Andrew Danforth + Mike Davis + Shie Erlich + Alexander von Gernler + Artur Grabowski + Aaron Hopkins + Claudio Jeker + Scott Lamb + Adam Langley + Philip Lewis + David Libenzi + Nick Mathewson + Andrey Matveev + Richard Nyberg + Jon Oberheide + Phil Oleson + Dave Pacheco + Tassilo von Parseval + Pierre Phaneuf + Jon Poland + Bert JW Regeer + Dug Song + Taral + +If I have forgotten your name, please contact me. diff --git a/libevent/WIN32-Code/event-config.h b/libevent/WIN32-Code/event-config.h new file mode 100644 index 00000000000..3059080274b --- /dev/null +++ b/libevent/WIN32-Code/event-config.h @@ -0,0 +1,244 @@ +/* event-config.h + * Generated by autoconf; post-processed by libevent. + * Do not edit this file. + * Do not rely on macros in this file existing in later versions. + */ +#ifndef _EVENT_CONFIG_H_ +#define _EVENT_CONFIG_H_ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if clock_gettime is available in libc */ +/* #undef _EVENT_DNS_USE_CPU_CLOCK_FOR_ID */ + +/* Define is no secure id variant is available */ +#define _EVENT_DNS_USE_GETTIMEOFDAY_FOR_ID 1 + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef _EVENT_HAVE_CLOCK_GETTIME */ + +/* Define if /dev/poll is available */ +/* #undef _EVENT_HAVE_DEVPOLL */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_DLFCN_H */ + +/* Define if your system supports the epoll system calls */ +/* #undef _EVENT_HAVE_EPOLL */ + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef _EVENT_HAVE_EPOLL_CTL */ + +/* Define if your system supports event ports */ +/* #undef _EVENT_HAVE_EVENT_PORTS */ + +/* Define to 1 if you have the `fcntl' function. */ +/* #undef _EVENT_HAVE_FCNTL */ + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +/* #undef _EVENT_HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `getnameinfo' function. */ +/* #undef _EVENT_HAVE_GETNAMEINFO */ + +/* Define to 1 if you have the `gettimeofday' function. */ +/* #define _EVENT_HAVE_GETTIMEOFDAY 1 */ + +/* Define to 1 if you have the `inet_ntop' function. */ +/* #undef _EVENT_HAVE_INET_NTOP */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_INTTYPES_H 1 */ + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef _EVENT_HAVE_KQUEUE */ + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +/* #undef _EVENT_HAVE_LIBNSL */ + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +/* #undef _EVENT_HAVE_LIBRESOLV */ + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef _EVENT_HAVE_LIBRT */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef _EVENT_HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_NETINET_IN6_H */ + +/* Define to 1 if you have the `poll' function. */ +/* #undef _EVENT_HAVE_POLL */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_POLL_H */ + +/* Define to 1 if you have the `port_create' function. */ +/* #undef _EVENT_HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +/* #undef _EVENT_HAVE_SELECT */ + +/* Define if F_SETFD is defined in */ +/* #undef _EVENT_HAVE_SETFD */ + +/* Define to 1 if you have the `sigaction' function. */ +/* #undef _EVENT_HAVE_SIGACTION */ + +/* Define to 1 if you have the `signal' function. */ +#define _EVENT_HAVE_SIGNAL 1 + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +/* #define _EVENT_HAVE_STDINT_H 1 */ + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef _EVENT_HAVE_STRLCPY */ + +/* Define to 1 if you have the `strsep' function. */ +/* #undef _EVENT_HAVE_STRSEP */ + +/* Define to 1 if you have the `strtok_r' function. */ +/* #undef _EVENT_HAVE_STRTOK_R */ + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define _EVENT_HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_DEVPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_IOCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_QUEUE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef _EVENT_HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define _EVENT_HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #define _EVENT_HAVE_SYS_TIME_H 1 */ + +/* Define to 1 if you have the header file. */ +/* #define _EVENT_HAVE_SYS_TYPES_H 1 */ + +/* Define if TAILQ_FOREACH is defined in */ +/* #undef _EVENT_HAVE_TAILQFOREACH */ + +/* Define if timeradd is defined in */ +/* #undef _EVENT_HAVE_TIMERADD */ + +/* Define if timerclear is defined in */ +/* #define _EVENT_HAVE_TIMERCLEAR 1 */ + +/* Define if timercmp is defined in */ +#define _EVENT_HAVE_TIMERCMP 1 + +/* Define if timerisset is defined in */ +#define _EVENT_HAVE_TIMERISSET 1 + +/* Define to 1 if you have the header file. */ +/* #define _EVENT_HAVE_UNISTD_H 1 */ + +/* Define to 1 if you have the `vasprintf' function. */ +/* #undef _EVENT_HAVE_VASPRINTF */ + +/* Define if kqueue works correctly with pipes */ +/* #undef _EVENT_HAVE_WORKING_KQUEUE */ + +/* Name of package */ +#define _EVENT_PACKAGE "libevent" + +/* Define to the address where bug reports for this package should be sent. */ +#define _EVENT_PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define _EVENT_PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define _EVENT_PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define _EVENT_PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define _EVENT_PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define _EVENT_STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define _EVENT_TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define _EVENT_VERSION "1.3.99-trunk" + +/* Define to appropriate substitue if compiler doesnt have __func__ */ +/* #undef _EVENT___func__ */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef _EVENT_const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef _EVENT___cplusplus +#define _EVENT_inline __inline +#endif + +/* Define to `int' if does not define. */ +/* #undef _EVENT_pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef _EVENT_size_t */ + +/* Define to unsigned int if you dont have it */ +#define _EVENT_socklen_t unsigned int + +/* Define to `unsigned short' if does not define. */ +/* #undef _EVENT_uint16_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef _EVENT_uint32_t */ + +/* Define to `unsigned long long' if does not define. */ +/* #undef _EVENT_uint64_t */ + +/* Define to `unsigned char' if does not define. */ +/* #undef _EVENT_uint8_t */ +#endif diff --git a/libevent/WIN32-Code/misc.c b/libevent/WIN32-Code/misc.c new file mode 100644 index 00000000000..371e192beae --- /dev/null +++ b/libevent/WIN32-Code/misc.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +/*our prototypes for timeval and timezone are in here, just in case the above + headers don't have them*/ +#include "misc.h" +#endif + +/**************************************************************************** + * + * Function: gettimeofday(struct timeval *, struct timezone *) + * + * Purpose: Get current time of day. + * + * Arguments: tv => Place to store the curent time of day. + * tz => Ignored. + * + * Returns: 0 => Success. + * + ****************************************************************************/ + +#ifndef HAVE_GETTIMEOFDAY +int gettimeofday(struct timeval *tv, struct timezone *tz) { + struct _timeb tb; + + if(tv == NULL) + return -1; + + _ftime(&tb); + tv->tv_sec = (long) tb.time; + tv->tv_usec = ((int) tb.millitm) * 1000; + return 0; +} +#endif + +#if 0 +int +win_read(int fd, void *buf, unsigned int length) +{ + DWORD dwBytesRead; + int res = ReadFile((HANDLE) fd, buf, length, &dwBytesRead, NULL); + if (res == 0) { + DWORD error = GetLastError(); + if (error == ERROR_NO_DATA) + return (0); + return (-1); + } else + return (dwBytesRead); +} + +int +win_write(int fd, void *buf, unsigned int length) +{ + DWORD dwBytesWritten; + int res = WriteFile((HANDLE) fd, buf, length, &dwBytesWritten, NULL); + if (res == 0) { + DWORD error = GetLastError(); + if (error == ERROR_NO_DATA) + return (0); + return (-1); + } else + return (dwBytesWritten); +} + +int +socketpair(int d, int type, int protocol, int *sv) +{ + static int count; + char buf[64]; + HANDLE fd; + DWORD dwMode; + sprintf(buf, "\\\\.\\pipe\\levent-%d", count++); + /* Create a duplex pipe which will behave like a socket pair */ + fd = CreateNamedPipe(buf, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_NOWAIT, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); + if (fd == INVALID_HANDLE_VALUE) + return (-1); + sv[0] = (int)fd; + + fd = CreateFile(buf, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) + return (-1); + dwMode = PIPE_NOWAIT; + SetNamedPipeHandleState(fd, &dwMode, NULL, NULL); + sv[1] = (int)fd; + + return (0); +} +#endif diff --git a/libevent/WIN32-Code/misc.h b/libevent/WIN32-Code/misc.h new file mode 100644 index 00000000000..aced574687c --- /dev/null +++ b/libevent/WIN32-Code/misc.h @@ -0,0 +1,11 @@ +#ifndef MISC_H +#define MISC_H + +struct timezone; +struct timeval; + +#ifndef HAVE_GETTIMEOFDAY +int gettimeofday(struct timeval *,struct timezone *); +#endif + +#endif diff --git a/libevent/WIN32-Code/tree.h b/libevent/WIN32-Code/tree.h new file mode 100644 index 00000000000..79e8d91f0eb --- /dev/null +++ b/libevent/WIN32-Code/tree.h @@ -0,0 +1,1354 @@ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* + * Copyright 2002 Niels Provos + * 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_ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* + * Copyright 2002 Niels Provos + * 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_ */ diff --git a/libevent/WIN32-Code/win32.c b/libevent/WIN32-Code/win32.c new file mode 100644 index 00000000000..8a603b7eceb --- /dev/null +++ b/libevent/WIN32-Code/win32.c @@ -0,0 +1,486 @@ +/* + * Copyright 2000-2002 Niels Provos + * Copyright 2003 Michael A. Davis + * 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 _MSC_VER +#include "./config.h" +#else +/* Avoid the windows/msvc thing. */ +#include "../config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RB_AUGMENT(x) (void)(x) +#include "./tree.h" +#include "log.h" +#include "event.h" +#include "event-internal.h" + +#define XFREE(ptr) do { if (ptr) free(ptr); } while(0) + +extern struct event_list timequeue; +extern struct event_list addqueue; +#if 0 +extern struct event_list signalqueue; +#endif + +struct win_fd_set { + u_int fd_count; + SOCKET fd_array[1]; +}; + +int evsigcaught[NSIG]; +volatile sig_atomic_t signal_caught = 0; +/* MSDN says this is required to handle SIGFPE */ +volatile double SIGFPE_REQ = 0.0f; + +#if 0 +static void signal_handler(int sig); + +void signal_process(void); +int signal_recalc(void); +#endif + +struct event_entry { + RB_ENTRY(event_entry) node; + SOCKET sock; + int read_pos; + int write_pos; + struct event *read_event; + struct event *write_event; +}; + +static int +compare(struct event_entry *a, struct event_entry *b) +{ + if (a->sock < b->sock) + return -1; + else if (a->sock > b->sock) + return 1; + else + return 0; +} + +struct win32op { + int fd_setsz; + struct win_fd_set *readset_in; + struct win_fd_set *writeset_in; + struct win_fd_set *readset_out; + struct win_fd_set *writeset_out; + struct win_fd_set *exset_out; + RB_HEAD(event_map, event_entry) event_root; + + unsigned signals_are_broken : 1; +}; + +RB_PROTOTYPE(event_map, event_entry, node, compare); +RB_GENERATE(event_map, event_entry, node, compare); + +void *win32_init (struct event_base *); +int win32_insert (void *, struct event *); +int win32_del (void *, struct event *); +int win32_dispatch (struct event_base *base, void *, struct timeval *); +void win32_dealloc (struct event_base *, void *); + +struct eventop win32ops = { + "win32", + win32_init, + win32_insert, + win32_del, + win32_dispatch, + win32_dealloc, + 0 +}; + +#define FD_SET_ALLOC_SIZE(n) ((sizeof(struct win_fd_set) + ((n)-1)*sizeof(SOCKET))) + +static int +realloc_fd_sets(struct win32op *op, size_t new_size) +{ + size_t size; + + assert(new_size >= op->readset_in->fd_count && + new_size >= op->writeset_in->fd_count); + assert(new_size >= 1); + + size = FD_SET_ALLOC_SIZE(new_size); + if (!(op->readset_in = realloc(op->readset_in, size))) + return (-1); + if (!(op->writeset_in = realloc(op->writeset_in, size))) + return (-1); + if (!(op->readset_out = realloc(op->readset_out, size))) + return (-1); + if (!(op->exset_out = realloc(op->exset_out, size))) + return (-1); + if (!(op->writeset_out = realloc(op->writeset_out, size))) + return (-1); + op->fd_setsz = new_size; + return (0); +} + +static int +timeval_to_ms(struct timeval *tv) +{ + return ((tv->tv_sec * 1000) + (tv->tv_usec / 1000)); +} + +static struct event_entry* +get_event_entry(struct win32op *op, SOCKET s, int create) +{ + struct event_entry key, *val; + key.sock = s; + val = RB_FIND(event_map, &op->event_root, &key); + if (val || !create) + return val; + if (!(val = calloc(1, sizeof(struct event_entry)))) { + event_warn("%s: calloc", __func__); + return NULL; + } + val->sock = s; + val->read_pos = val->write_pos = -1; + RB_INSERT(event_map, &op->event_root, val); + return val; +} + +static int +do_fd_set(struct win32op *op, struct event_entry *ent, int read) +{ + SOCKET s = ent->sock; + struct win_fd_set *set = read ? op->readset_in : op->writeset_in; + if (read) { + if (ent->read_pos >= 0) + return (0); + } else { + if (ent->write_pos >= 0) + return (0); + } + if (set->fd_count == op->fd_setsz) { + if (realloc_fd_sets(op, op->fd_setsz*2)) + return (-1); + /* set pointer will have changed and needs reiniting! */ + set = read ? op->readset_in : op->writeset_in; + } + set->fd_array[set->fd_count] = s; + if (read) + ent->read_pos = set->fd_count; + else + ent->write_pos = set->fd_count; + return (set->fd_count++); +} + +static int +do_fd_clear(struct win32op *op, struct event_entry *ent, int read) +{ + int i; + struct win_fd_set *set = read ? op->readset_in : op->writeset_in; + if (read) { + i = ent->read_pos; + ent->read_pos = -1; + } else { + i = ent->write_pos; + ent->write_pos = -1; + } + if (i < 0) + return (0); + if (--set->fd_count != i) { + struct event_entry *ent2; + SOCKET s2; + s2 = set->fd_array[i] = set->fd_array[set->fd_count]; + ent2 = get_event_entry(op, s2, 0); + if (!ent) /* This indicates a bug. */ + return (0); + if (read) + ent2->read_pos = i; + else + ent2->write_pos = i; + } + return (0); +} + +#define NEVENT 64 +void * +win32_init(struct event_base *_base) +{ + struct win32op *winop; + size_t size; + if (!(winop = calloc(1, sizeof(struct win32op)))) + return NULL; + winop->fd_setsz = NEVENT; + size = FD_SET_ALLOC_SIZE(NEVENT); + if (!(winop->readset_in = malloc(size))) + goto err; + if (!(winop->writeset_in = malloc(size))) + goto err; + if (!(winop->readset_out = malloc(size))) + goto err; + if (!(winop->writeset_out = malloc(size))) + goto err; + if (!(winop->exset_out = malloc(size))) + goto err; + RB_INIT(&winop->event_root); + winop->readset_in->fd_count = winop->writeset_in->fd_count = 0; + winop->readset_out->fd_count = winop->writeset_out->fd_count + = winop->exset_out->fd_count = 0; + + if (evsignal_init(_base) < 0) + winop->signals_are_broken = 1; + + return (winop); + err: + XFREE(winop->readset_in); + XFREE(winop->writeset_in); + XFREE(winop->readset_out); + XFREE(winop->writeset_out); + XFREE(winop->exset_out); + XFREE(winop); + return (NULL); +} + +int +win32_insert(void *op, struct event *ev) +{ + struct win32op *win32op = op; + struct event_entry *ent; + + if (ev->ev_events & EV_SIGNAL) { + if (win32op->signals_are_broken) + return (-1); + return (evsignal_add(ev)); + } + if (!(ev->ev_events & (EV_READ|EV_WRITE))) + return (0); + ent = get_event_entry(win32op, ev->ev_fd, 1); + if (!ent) + return (-1); /* out of memory */ + + event_debug(("%s: adding event for %d", __func__, (int)ev->ev_fd)); + if (ev->ev_events & EV_READ) { + if (do_fd_set(win32op, ent, 1)<0) + return (-1); + ent->read_event = ev; + } + if (ev->ev_events & EV_WRITE) { + if (do_fd_set(win32op, ent, 0)<0) + return (-1); + ent->write_event = ev; + } + return (0); +} + +int +win32_del(void *op, struct event *ev) +{ + struct win32op *win32op = op; + struct event_entry *ent; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(ev)); + + if (!(ent = get_event_entry(win32op, ev->ev_fd, 0))) + return (-1); + event_debug(("%s: Removing event for %d", __func__, ev->ev_fd)); + if (ev == ent->read_event) { + do_fd_clear(win32op, ent, 1); + ent->read_event = NULL; + } + if (ev == ent->write_event) { + do_fd_clear(win32op, ent, 0); + ent->write_event = NULL; + } + if (!ent->read_event && !ent->write_event) { + RB_REMOVE(event_map, &win32op->event_root, ent); + free(ent); + } + + return 0; +} + +static void +fd_set_copy(struct win_fd_set *out, const struct win_fd_set *in) +{ + out->fd_count = in->fd_count; + memcpy(out->fd_array, in->fd_array, in->fd_count * (sizeof(SOCKET))); +} + +/* + static void dump_fd_set(struct win_fd_set *s) + { + unsigned int i; + printf("[ "); + for(i=0;ifd_count;++i) + printf("%d ",(int)s->fd_array[i]); + printf("]\n"); + } +*/ + +int +win32_dispatch(struct event_base *base, void *op, + struct timeval *tv) +{ + struct win32op *win32op = op; + int res = 0; + unsigned j, i; + int fd_count; + SOCKET s; + struct event_entry *ent; + + fd_set_copy(win32op->readset_out, win32op->readset_in); + fd_set_copy(win32op->exset_out, win32op->readset_in); + fd_set_copy(win32op->writeset_out, win32op->writeset_in); + + fd_count = + (win32op->readset_out->fd_count > win32op->writeset_out->fd_count) ? + win32op->readset_out->fd_count : win32op->writeset_out->fd_count; + + if (!fd_count) { + /* Windows doesn't like you to call select() with no sockets */ + Sleep(timeval_to_ms(tv)); + evsignal_process(base); + return (0); + } + + res = select(fd_count, + (struct fd_set*)win32op->readset_out, + (struct fd_set*)win32op->writeset_out, + (struct fd_set*)win32op->exset_out, tv); + + event_debug(("%s: select returned %d", __func__, res)); + + if(res <= 0) { + evsignal_process(base); + return res; + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + if (win32op->readset_out->fd_count) { + i = rand() % win32op->readset_out->fd_count; + for (j=0; jreadset_out->fd_count; ++j) { + if (++i >= win32op->readset_out->fd_count) + i = 0; + s = win32op->readset_out->fd_array[i]; + if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event) + event_active(ent->read_event, EV_READ, 1); + } + } + if (win32op->exset_out->fd_count) { + i = rand() % win32op->exset_out->fd_count; + for (j=0; jexset_out->fd_count; ++j) { + if (++i >= win32op->exset_out->fd_count) + i = 0; + s = win32op->exset_out->fd_array[i]; + if ((ent = get_event_entry(win32op, s, 0)) && ent->read_event) + event_active(ent->read_event, EV_READ, 1); + } + } + if (win32op->writeset_out->fd_count) { + i = rand() % win32op->writeset_out->fd_count; + for (j=0; jwriteset_out->fd_count; ++j) { + if (++i >= win32op->exset_out->fd_count) + i = 0; + s = win32op->writeset_out->fd_array[i]; + if ((ent = get_event_entry(win32op, s, 0)) && ent->write_event) + event_active(ent->write_event, EV_WRITE, 1); + + } + } + + return (0); +} + +void +win32_dealloc(struct event_base *_base, void *arg) +{ + struct win32op *win32op = arg; + + evsignal_dealloc(_base); + if (win32op->readset_in) + free(win32op->readset_in); + if (win32op->writeset_in) + free(win32op->writeset_in); + if (win32op->readset_out) + free(win32op->readset_out); + if (win32op->writeset_out) + free(win32op->writeset_out); + if (win32op->exset_out) + free(win32op->exset_out); + /* XXXXX free the tree. */ + + memset(win32op, 0, sizeof(win32op)); + free(win32op); +} + +#if 0 +static void +signal_handler(int sig) +{ + evsigcaught[sig]++; + signal_caught = 1; +} + +int +signal_recalc(void) +{ + struct event *ev; + + /* Reinstall our signal handler. */ + TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { + if((int)signal(EVENT_SIGNAL(ev), signal_handler) == -1) + return (-1); + } + return (0); +} + +void +signal_process(void) +{ + struct event *ev; + short ncalls; + + TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) { + ncalls = evsigcaught[EVENT_SIGNAL(ev)]; + if (ncalls) { + if (!(ev->ev_events & EV_PERSIST)) + event_del(ev); + event_active(ev, EV_SIGNAL, ncalls); + } + } + + memset(evsigcaught, 0, sizeof(evsigcaught)); + signal_caught = 0; +} +#endif + diff --git a/libevent/WIN32-Prj/libevent.dsw b/libevent/WIN32-Prj/libevent.dsw new file mode 100644 index 00000000000..fb05451ca25 --- /dev/null +++ b/libevent/WIN32-Prj/libevent.dsw @@ -0,0 +1,74 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "event_test"=".\event_test\event_test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libevent + End Project Dependency +}}} + +############################################################################### + +Project: "libevent"=".\libevent.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "signal_test"=".\signal_test\signal_test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libevent + End Project Dependency +}}} + +############################################################################### + +Project: "time_test"=".\time_test\time_test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libevent + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/libevent/WIN32-Prj/libevent.sln b/libevent/WIN32-Prj/libevent.sln new file mode 100644 index 00000000000..17e0c98bae6 --- /dev/null +++ b/libevent/WIN32-Prj/libevent.sln @@ -0,0 +1,53 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "event_test", "event_test\event_test.vcproj", "{52099A8B-455B-4BE9-8E61-A3D6E8A4338D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libevent", "libevent.vcproj", "{B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "signal_test", "signal_test\signal_test.vcproj", "{768DB9DD-2694-4274-89B8-74106E8F7786}" + ProjectSection(ProjectDependencies) = postProject + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} = {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "time_test", "time_test\time_test.vcproj", "{D4BE29FB-E45C-4177-9647-74BBAFDC1257}" + ProjectSection(ProjectDependencies) = postProject + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} = {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "regress", "regress\regress.vcproj", "{F7C26008-6066-4AD3-8543-452EFE58BD2E}" + ProjectSection(ProjectDependencies) = postProject + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} = {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {52099A8B-455B-4BE9-8E61-A3D6E8A4338D}.Debug|Win32.ActiveCfg = Debug|Win32 + {52099A8B-455B-4BE9-8E61-A3D6E8A4338D}.Debug|Win32.Build.0 = Debug|Win32 + {52099A8B-455B-4BE9-8E61-A3D6E8A4338D}.Release|Win32.ActiveCfg = Release|Win32 + {52099A8B-455B-4BE9-8E61-A3D6E8A4338D}.Release|Win32.Build.0 = Release|Win32 + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9}.Debug|Win32.ActiveCfg = Debug|Win32 + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9}.Debug|Win32.Build.0 = Debug|Win32 + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9}.Release|Win32.ActiveCfg = Release|Win32 + {B98ABFCE-24D4-4B70-94DE-EF7F1E0662F9}.Release|Win32.Build.0 = Release|Win32 + {768DB9DD-2694-4274-89B8-74106E8F7786}.Debug|Win32.ActiveCfg = Debug|Win32 + {768DB9DD-2694-4274-89B8-74106E8F7786}.Debug|Win32.Build.0 = Debug|Win32 + {768DB9DD-2694-4274-89B8-74106E8F7786}.Release|Win32.ActiveCfg = Release|Win32 + {768DB9DD-2694-4274-89B8-74106E8F7786}.Release|Win32.Build.0 = Release|Win32 + {D4BE29FB-E45C-4177-9647-74BBAFDC1257}.Debug|Win32.ActiveCfg = Debug|Win32 + {D4BE29FB-E45C-4177-9647-74BBAFDC1257}.Debug|Win32.Build.0 = Debug|Win32 + {D4BE29FB-E45C-4177-9647-74BBAFDC1257}.Release|Win32.ActiveCfg = Release|Win32 + {D4BE29FB-E45C-4177-9647-74BBAFDC1257}.Release|Win32.Build.0 = Release|Win32 + {F7C26008-6066-4AD3-8543-452EFE58BD2E}.Debug|Win32.ActiveCfg = Debug|Win32 + {F7C26008-6066-4AD3-8543-452EFE58BD2E}.Debug|Win32.Build.0 = Debug|Win32 + {F7C26008-6066-4AD3-8543-452EFE58BD2E}.Release|Win32.ActiveCfg = Release|Win32 + {F7C26008-6066-4AD3-8543-452EFE58BD2E}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libevent/autogen.sh b/libevent/autogen.sh new file mode 100644 index 00000000000..6d4275a6392 --- /dev/null +++ b/libevent/autogen.sh @@ -0,0 +1,11 @@ +#!/bin/sh +LIBTOOLIZE=libtoolize +SYSNAME=`uname` +if [ "x$SYSNAME" = "xDarwin" ] ; then + LIBTOOLIZE=glibtoolize +fi +aclocal && \ + autoheader && \ + $LIBTOOLIZE && \ + autoconf && \ + automake --add-missing --copy diff --git a/libevent/buffer.c b/libevent/buffer.c new file mode 100644 index 00000000000..9cb0f0ce323 --- /dev/null +++ b/libevent/buffer.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2002, 2003 Niels Provos + * 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 +#include +#include +#endif + +#ifdef HAVE_VASPRINTF +/* If we have vasprintf, we need to define this before we include stdio.h. */ +#define _GNU_SOURCE +#endif + +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "event.h" +#include "config.h" +#include "evutil.h" + +struct evbuffer * +evbuffer_new(void) +{ + struct evbuffer *buffer; + + buffer = calloc(1, sizeof(struct evbuffer)); + + return (buffer); +} + +void +evbuffer_free(struct evbuffer *buffer) +{ + if (buffer->orig_buffer != NULL) + free(buffer->orig_buffer); + free(buffer); +} + +/* + * This is a destructive add. The data from one buffer moves into + * the other buffer. + */ + +#define SWAP(x,y) do { \ + (x)->buffer = (y)->buffer; \ + (x)->orig_buffer = (y)->orig_buffer; \ + (x)->misalign = (y)->misalign; \ + (x)->totallen = (y)->totallen; \ + (x)->off = (y)->off; \ +} while (0) + +int +evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf) +{ + int res; + + /* Short cut for better performance */ + if (outbuf->off == 0) { + struct evbuffer tmp; + size_t oldoff = inbuf->off; + + /* Swap them directly */ + SWAP(&tmp, outbuf); + SWAP(outbuf, inbuf); + SWAP(inbuf, &tmp); + + /* + * Optimization comes with a price; we need to notify the + * buffer if necessary of the changes. oldoff is the amount + * of data that we transfered from inbuf to outbuf + */ + if (inbuf->off != oldoff && inbuf->cb != NULL) + (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg); + if (oldoff && outbuf->cb != NULL) + (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg); + + return (0); + } + + res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off); + if (res == 0) { + /* We drain the input buffer on success */ + evbuffer_drain(inbuf, inbuf->off); + } + + return (res); +} + +int +evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap) +{ + char *buffer; + size_t space; + size_t oldoff = buf->off; + int sz; + va_list aq; + + /* make sure that at least some space is available */ + evbuffer_expand(buf, 64); + for (;;) { + size_t used = buf->misalign + buf->off; + buffer = (char *)buf->buffer + buf->off; + assert(buf->totallen >= used); + space = buf->totallen - used; + +#ifndef va_copy +#define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) +#endif + va_copy(aq, ap); + + sz = evutil_vsnprintf(buffer, space, fmt, aq); + + va_end(aq); + + if (sz < 0) + return (-1); + if ((size_t)sz < space) { + buf->off += sz; + if (buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + return (sz); + } + if (evbuffer_expand(buf, sz + 1) == -1) + return (-1); + + } + /* NOTREACHED */ +} + +int +evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...) +{ + int res = -1; + va_list ap; + + va_start(ap, fmt); + res = evbuffer_add_vprintf(buf, fmt, ap); + va_end(ap); + + return (res); +} + +/* Reads data from an event buffer and drains the bytes read */ + +int +evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen) +{ + size_t nread = datlen; + if (nread >= buf->off) + nread = buf->off; + + memcpy(data, buf->buffer, nread); + evbuffer_drain(buf, nread); + + return (nread); +} + +/* + * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. + * The returned buffer needs to be freed by the called. + */ + +char * +evbuffer_readline(struct evbuffer *buffer) +{ + u_char *data = EVBUFFER_DATA(buffer); + size_t len = EVBUFFER_LENGTH(buffer); + char *line; + unsigned int i; + + for (i = 0; i < len; i++) { + if (data[i] == '\r' || data[i] == '\n') + break; + } + + if (i == len) + return (NULL); + + if ((line = malloc(i + 1)) == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + evbuffer_drain(buffer, i); + return (NULL); + } + + memcpy(line, data, i); + line[i] = '\0'; + + /* + * Some protocols terminate a line with '\r\n', so check for + * that, too. + */ + if ( i < len - 1 ) { + char fch = data[i], sch = data[i+1]; + + /* Drain one more character if needed */ + if ( (sch == '\r' || sch == '\n') && sch != fch ) + i += 1; + } + + evbuffer_drain(buffer, i + 1); + + return (line); +} + +/* Adds data to an event buffer */ + +static void +evbuffer_align(struct evbuffer *buf) +{ + memmove(buf->orig_buffer, buf->buffer, buf->off); + buf->buffer = buf->orig_buffer; + buf->misalign = 0; +} + +/* Expands the available space in the event buffer to at least datlen */ + +int +evbuffer_expand(struct evbuffer *buf, size_t datlen) +{ + size_t need = buf->misalign + buf->off + datlen; + + /* If we can fit all the data, then we don't have to do anything */ + if (buf->totallen >= need) + return (0); + + /* + * If the misalignment fulfills our data needs, we just force an + * alignment to happen. Afterwards, we have enough space. + */ + if (buf->misalign >= datlen) { + evbuffer_align(buf); + } else { + void *newbuf; + size_t length = buf->totallen; + + if (length < 256) + length = 256; + while (length < need) + length <<= 1; + + if (buf->orig_buffer != buf->buffer) + evbuffer_align(buf); + if ((newbuf = realloc(buf->buffer, length)) == NULL) + return (-1); + + buf->orig_buffer = buf->buffer = newbuf; + buf->totallen = length; + } + + return (0); +} + +int +evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) +{ + size_t need = buf->misalign + buf->off + datlen; + size_t oldoff = buf->off; + + if (buf->totallen < need) { + if (evbuffer_expand(buf, datlen) == -1) + return (-1); + } + + memcpy(buf->buffer + buf->off, data, datlen); + buf->off += datlen; + + if (datlen && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + + return (0); +} + +void +evbuffer_drain(struct evbuffer *buf, size_t len) +{ + size_t oldoff = buf->off; + + if (len >= buf->off) { + buf->off = 0; + buf->buffer = buf->orig_buffer; + buf->misalign = 0; + goto done; + } + + buf->buffer += len; + buf->misalign += len; + + buf->off -= len; + + done: + /* Tell someone about changes in this buffer */ + if (buf->off != oldoff && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + +} + +/* + * Reads data from a file descriptor into a buffer. + */ + +#define EVBUFFER_MAX_READ 4096 + +int +evbuffer_read(struct evbuffer *buf, int fd, int howmuch) +{ + u_char *p; + size_t oldoff = buf->off; + int n = EVBUFFER_MAX_READ; + +#if defined(FIONREAD) +#ifdef WIN32 + 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) { + /* + * It's possible that a lot of data is available for + * reading. We do not want to exhaust resources + * before the reader has a chance to do something + * about it. If the reader does not tell us how much + * data we should read, we artifically limit it. + */ + if ((size_t)n > buf->totallen << 2) + n = buf->totallen << 2; + if (n < EVBUFFER_MAX_READ) + n = EVBUFFER_MAX_READ; + } +#endif + if (howmuch < 0 || howmuch > n) + howmuch = n; + + /* If we don't have FIONREAD, we might waste some space here */ + if (evbuffer_expand(buf, howmuch) == -1) + return (-1); + + /* We can append new data at this point */ + p = buf->buffer + buf->off; + +#ifndef WIN32 + n = read(fd, p, howmuch); +#else + n = recv(fd, p, howmuch, 0); +#endif + if (n == -1) + return (-1); + if (n == 0) + return (0); + + buf->off += n; + + /* Tell someone about changes in this buffer */ + if (buf->off != oldoff && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + + return (n); +} + +int +evbuffer_write(struct evbuffer *buffer, int fd) +{ + int n; + +#ifndef WIN32 + n = write(fd, buffer->buffer, buffer->off); +#else + 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); +} + +u_char * +evbuffer_find(struct evbuffer *buffer, const u_char *what, size_t len) +{ + u_char *search = buffer->buffer, *end = search + buffer->off; + u_char *p; + + while (search < end && + (p = memchr(search, *what, end - search)) != NULL) { + if (p + len > end) + break; + if (memcmp(p, what, len) == 0) + return (p); + search = p + 1; + } + + return (NULL); +} + +void evbuffer_setcb(struct evbuffer *buffer, + void (*cb)(struct evbuffer *, size_t, size_t, void *), + void *cbarg) +{ + buffer->cb = cb; + buffer->cbarg = cbarg; +} diff --git a/libevent/compat/sys/_time.h b/libevent/compat/sys/_time.h new file mode 100644 index 00000000000..8cabb0d55e7 --- /dev/null +++ b/libevent/compat/sys/_time.h @@ -0,0 +1,163 @@ +/* $OpenBSD: time.h,v 1.11 2000/10/10 13:36:48 itojun Exp $ */ +/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)time.h 8.2 (Berkeley) 7/10/94 + */ + +#ifndef _SYS_TIME_H_ +#define _SYS_TIME_H_ + +#include + +/* + * Structure returned by gettimeofday(2) system call, + * and used in other calls. + */ +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +/* + * Structure defined by POSIX.1b to be like a timeval. + */ +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* and nanoseconds */ +}; + +#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ +} + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; +#define DST_NONE 0 /* not on dst */ +#define DST_USA 1 /* USA style dst */ +#define DST_AUST 2 /* Australian style dst */ +#define DST_WET 3 /* Western European dst */ +#define DST_MET 4 /* Middle European dst */ +#define DST_EET 5 /* Eastern European dst */ +#define DST_CAN 6 /* Canada */ + +/* Operations on timevals. */ +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#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)) +#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) + +/* Operations on timespecs. */ +#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0 +#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) + +/* + * Names of the interval timers, and structure + * defining a timer setting. + */ +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct itimerval { + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; + +/* + * Getkerninfo clock information structure + */ +struct clockinfo { + int hz; /* clock frequency */ + int tick; /* micro-seconds per hz tick */ + int tickadj; /* clock skew rate for adjtime() */ + int stathz; /* statistics clock frequency */ + int profhz; /* profiling clock frequency */ +}; + +#define CLOCK_REALTIME 0 +#define CLOCK_VIRTUAL 1 +#define CLOCK_PROF 2 + +#define TIMER_RELTIME 0x0 /* relative timer */ +#define TIMER_ABSTIME 0x1 /* absolute timer */ + +/* --- stuff got cut here - niels --- */ + +#endif /* !_SYS_TIME_H_ */ diff --git a/libevent/compat/sys/queue.h b/libevent/compat/sys/queue.h new file mode 100644 index 00000000000..c0956ddce43 --- /dev/null +++ b/libevent/compat/sys/queue.h @@ -0,0 +1,488 @@ +/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#ifndef WIN32 +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} +#endif + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \ + if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#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) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/libevent/configure.in b/libevent/configure.in new file mode 100644 index 00000000000..bc3eca1f043 --- /dev/null +++ b/libevent/configure.in @@ -0,0 +1,387 @@ +dnl configure.in for libevent +dnl Dug Song +AC_INIT(event.c) + +AM_INIT_AUTOMAKE(libevent,1.4.12-stable) +AM_CONFIG_HEADER(config.h) +dnl AM_MAINTAINER_MODE + +dnl Initialize prefix. +if test "$prefix" = "NONE"; then + prefix="/usr/local" +fi + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S + +AC_PROG_GCC_TRADITIONAL +if test "$GCC" = yes ; then + CFLAGS="$CFLAGS -Wall" + # And disable the strict-aliasing optimization, since it breaks + # our sockaddr-handling code in strange ways. + CFLAGS="$CFLAGS -fno-strict-aliasing" +fi + +AC_ARG_ENABLE(gcc-warnings, + AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings with GCC)) + +AC_PROG_LIBTOOL + +dnl Uncomment "AC_DISABLE_SHARED" to make shared librraries not get +dnl built by default. You can also turn shared libs on and off from +dnl the command line with --enable-shared and --disable-shared. +dnl AC_DISABLE_SHARED +AC_SUBST(LIBTOOL_DEPS) + +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 +#ifdef TAILQ_FOREACH + yes +#endif +], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_TAILQFOREACH, 1, + [Define if TAILQ_FOREACH is defined in ])], + 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 +#ifdef timeradd + yes +#endif +], [ AC_DEFINE(HAVE_TIMERADD, 1, + [Define if timeradd is defined in ]) + 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 +#ifdef timercmp + yes +#endif +], [ AC_DEFINE(HAVE_TIMERCMP, 1, + [Define if timercmp is defined in ]) + 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 +#ifdef timerclear + yes +#endif +], [ AC_DEFINE(HAVE_TIMERCLEAR, 1, + [Define if timerclear is defined in ]) + 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 +#ifdef timerisset + yes +#endif +], [ AC_DEFINE(HAVE_TIMERISSET, 1, + [Define if timerisset is defined in ]) + AC_MSG_RESULT(yes)] ,AC_MSG_RESULT(no) +) +fi + +dnl - check if the macro WIN32 is defined on this compiler. +dnl - (this is how we check for a windows version of GCC) +AC_MSG_CHECKING(for WIN32) +AC_TRY_COMPILE(, + [ +#ifndef WIN32 +die horribly +#endif + ], + bwin32=true; AC_MSG_RESULT(yes), + bwin32=false; AC_MSG_RESULT(no), +) + +AM_CONDITIONAL(BUILD_WIN32, test x$bwin32 = xtrue) + +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 +#ifdef F_SETFD +yes +#endif +], [ AC_DEFINE(HAVE_SETFD, 1, + [Define if F_SETFD is defined in ]) + 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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + +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 +#elif defined(HAVE_INTTYPES_H) +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif]) +AC_CHECK_SIZEOF(long long) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(short) +AC_CHECK_TYPES([struct in6_addr], , , +[#ifdef WIN32 +#include +#else +#include +#include +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif]) + +AC_MSG_CHECKING([for socklen_t]) +AC_TRY_COMPILE([ + #include + #include ], + [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__]))) + + +# Add some more warnings which we use in development but not in the +# released versions. (Some relevant gcc versions can't handle these.) +if test x$enable_gcc_warnings = xyes; then + + AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 4) +#error +#endif]), have_gcc4=yes, have_gcc4=no) + + AC_COMPILE_IFELSE(AC_LANG_PROGRAM([], [ +#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 2) +#error +#endif]), have_gcc42=yes, have_gcc42=no) + + CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2 -Wwrite-strings -Wmissing-declarations -Wredundant-decls -Wnested-externs -Wbad-function-cast -Wswitch-enum -Werror" + CFLAGS="$CFLAGS -Wno-unused-parameter -Wno-sign-compare -Wstrict-aliasing" + + if test x$have_gcc4 = xyes ; then + # These warnings break gcc 3.3.5 and work on gcc 4.0.2 + CFLAGS="$CFLAGS -Winit-self -Wmissing-field-initializers -Wdeclaration-after-statement" + #CFLAGS="$CFLAGS -Wold-style-definition" + fi + + if test x$have_gcc42 = xyes ; then + # These warnings break gcc 4.0.2 and work on gcc 4.2 + CFLAGS="$CFLAGS -Waddress -Wnormalized=id -Woverride-init" + fi + +##This will break the world on some 64-bit architectures +# CFLAGS="$CFLAGS -Winline" + +fi + +AC_OUTPUT(Makefile test/Makefile sample/Makefile) diff --git a/libevent/devpoll.c b/libevent/devpoll.c new file mode 100644 index 00000000000..cbd27309079 --- /dev/null +++ b/libevent/devpoll.c @@ -0,0 +1,417 @@ +/* + * Copyright 2000-2004 Niels Provos + * 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 + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "event-internal.h" +#include "evsignal.h" +#include "log.h" + +/* due to limitations in the devpoll interface, we need to keep track of + * all file descriptors outself. + */ +struct evdevpoll { + struct event *evread; + struct event *evwrite; +}; + +struct devpollop { + struct evdevpoll *fds; + int nfds; + struct pollfd *events; + int nevents; + int dpfd; + struct pollfd *changes; + int nchanges; +}; + +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 *); + +const struct eventop devpollops = { + "devpoll", + devpoll_init, + devpoll_add, + devpoll_del, + devpoll_dispatch, + devpoll_dealloc, + 1 /* need reinit */ +}; + +#define NEVENT 32000 + +static int +devpoll_commit(struct devpollop *devpollop) +{ + /* + * Due to a bug in Solaris, we have to use pwrite with an offset of 0. + * Write is limited to 2GB of data, until it will fail. + */ + if (pwrite(devpollop->dpfd, devpollop->changes, + sizeof(struct pollfd) * devpollop->nchanges, 0) == -1) + return(-1); + + devpollop->nchanges = 0; + return(0); +} + +static int +devpoll_queue(struct devpollop *devpollop, int fd, int events) { + struct pollfd *pfd; + + if (devpollop->nchanges >= devpollop->nevents) { + /* + * Change buffer is full, must commit it to /dev/poll before + * adding more + */ + if (devpoll_commit(devpollop) != 0) + return(-1); + } + + pfd = &devpollop->changes[devpollop->nchanges++]; + pfd->fd = fd; + pfd->events = events; + pfd->revents = 0; + + return(0); +} + +static void * +devpoll_init(struct event_base *base) +{ + int dpfd, nfiles = NEVENT; + struct rlimit rl; + struct devpollop *devpollop; + + /* Disable devpoll when this environment variable is set */ + if (getenv("EVENT_NODEVPOLL")) + return (NULL); + + if (!(devpollop = calloc(1, sizeof(struct devpollop)))) + return (NULL); + + if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && + rl.rlim_cur != RLIM_INFINITY) + nfiles = rl.rlim_cur; + + /* Initialize the kernel queue */ + if ((dpfd = open("/dev/poll", O_RDWR)) == -1) { + event_warn("open: /dev/poll"); + free(devpollop); + return (NULL); + } + + devpollop->dpfd = dpfd; + + /* Initialize fields */ + devpollop->events = calloc(nfiles, sizeof(struct pollfd)); + if (devpollop->events == NULL) { + free(devpollop); + close(dpfd); + return (NULL); + } + devpollop->nevents = nfiles; + + devpollop->fds = calloc(nfiles, sizeof(struct evdevpoll)); + if (devpollop->fds == NULL) { + free(devpollop->events); + free(devpollop); + close(dpfd); + return (NULL); + } + devpollop->nfds = nfiles; + + devpollop->changes = calloc(nfiles, sizeof(struct pollfd)); + if (devpollop->changes == NULL) { + free(devpollop->fds); + free(devpollop->events); + free(devpollop); + close(dpfd); + return (NULL); + } + + evsignal_init(base); + + return (devpollop); +} + +static int +devpoll_recalc(struct event_base *base, void *arg, int max) +{ + struct devpollop *devpollop = arg; + + if (max >= devpollop->nfds) { + struct evdevpoll *fds; + int nfds; + + nfds = devpollop->nfds; + while (nfds <= max) + nfds <<= 1; + + fds = realloc(devpollop->fds, nfds * sizeof(struct evdevpoll)); + if (fds == NULL) { + event_warn("realloc"); + return (-1); + } + devpollop->fds = fds; + memset(fds + devpollop->nfds, 0, + (nfds - devpollop->nfds) * sizeof(struct evdevpoll)); + devpollop->nfds = nfds; + } + + return (0); +} + +static int +devpoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + struct devpollop *devpollop = arg; + struct pollfd *events = devpollop->events; + struct dvpoll dvp; + struct evdevpoll *evdp; + int i, res, timeout = -1; + + if (devpollop->nchanges) + devpoll_commit(devpollop); + + if (tv != NULL) + timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + + dvp.dp_fds = devpollop->events; + dvp.dp_nfds = devpollop->nevents; + dvp.dp_timeout = timeout; + + res = ioctl(devpollop->dpfd, DP_POLL, &dvp); + + if (res == -1) { + if (errno != EINTR) { + event_warn("ioctl: DP_POLL"); + return (-1); + } + + evsignal_process(base); + return (0); + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + event_debug(("%s: devpoll_wait reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int which = 0; + int what = events[i].revents; + struct event *evread = NULL, *evwrite = NULL; + + assert(events[i].fd < devpollop->nfds); + evdp = &devpollop->fds[events[i].fd]; + + if (what & POLLHUP) + what |= POLLIN | POLLOUT; + else if (what & POLLERR) + what |= POLLIN | POLLOUT; + + if (what & POLLIN) { + evread = evdp->evread; + which |= EV_READ; + } + + if (what & POLLOUT) { + evwrite = evdp->evwrite; + which |= EV_WRITE; + } + + if (!which) + 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) + event_active(evwrite, EV_WRITE, 1); + } + + return (0); +} + + +static int +devpoll_add(void *arg, struct event *ev) +{ + struct devpollop *devpollop = arg; + struct evdevpoll *evdp; + int fd, events; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(ev)); + + fd = ev->ev_fd; + if (fd >= devpollop->nfds) { + /* Extend the file descriptor array as necessary */ + if (devpoll_recalc(ev->ev_base, devpollop, fd) == -1) + return (-1); + } + evdp = &devpollop->fds[fd]; + + /* + * It's not necessary to OR the existing read/write events that we + * are currently interested in with the new event we are adding. + * The /dev/poll driver ORs any new events with the existing events + * that it has cached for the fd. + */ + + events = 0; + if (ev->ev_events & EV_READ) { + if (evdp->evread && evdp->evread != ev) { + /* There is already a different read event registered */ + return(-1); + } + events |= POLLIN; + } + + if (ev->ev_events & EV_WRITE) { + if (evdp->evwrite && evdp->evwrite != ev) { + /* There is already a different write event registered */ + return(-1); + } + events |= POLLOUT; + } + + if (devpoll_queue(devpollop, fd, events) != 0) + return(-1); + + /* Update events responsible */ + if (ev->ev_events & EV_READ) + evdp->evread = ev; + if (ev->ev_events & EV_WRITE) + evdp->evwrite = ev; + + return (0); +} + +static int +devpoll_del(void *arg, struct event *ev) +{ + struct devpollop *devpollop = arg; + struct evdevpoll *evdp; + int fd, events; + int needwritedelete = 1, needreaddelete = 1; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(ev)); + + fd = ev->ev_fd; + if (fd >= devpollop->nfds) + return (0); + evdp = &devpollop->fds[fd]; + + events = 0; + if (ev->ev_events & EV_READ) + events |= POLLIN; + if (ev->ev_events & EV_WRITE) + events |= POLLOUT; + + /* + * The only way to remove an fd from the /dev/poll monitored set is + * to use POLLREMOVE by itself. This removes ALL events for the fd + * provided so if we care about two events and are only removing one + * we must re-add the other event after POLLREMOVE. + */ + + if (devpoll_queue(devpollop, fd, POLLREMOVE) != 0) + return(-1); + + if ((events & (POLLIN|POLLOUT)) != (POLLIN|POLLOUT)) { + /* + * We're not deleting all events, so we must resubmit the + * event that we are still interested in if one exists. + */ + + if ((events & POLLIN) && evdp->evwrite != NULL) { + /* Deleting read, still care about write */ + devpoll_queue(devpollop, fd, POLLOUT); + needwritedelete = 0; + } else if ((events & POLLOUT) && evdp->evread != NULL) { + /* Deleting write, still care about read */ + devpoll_queue(devpollop, fd, POLLIN); + needreaddelete = 0; + } + } + + if (needreaddelete) + evdp->evread = NULL; + if (needwritedelete) + evdp->evwrite = NULL; + + return (0); +} + +static void +devpoll_dealloc(struct event_base *base, void *arg) +{ + struct devpollop *devpollop = arg; + + evsignal_dealloc(base); + if (devpollop->fds) + free(devpollop->fds); + if (devpollop->events) + free(devpollop->events); + if (devpollop->changes) + free(devpollop->changes); + if (devpollop->dpfd >= 0) + close(devpollop->dpfd); + + memset(devpollop, 0, sizeof(struct devpollop)); + free(devpollop); +} diff --git a/libevent/epoll.c b/libevent/epoll.c new file mode 100644 index 00000000000..b479b9c07e9 --- /dev/null +++ b/libevent/epoll.c @@ -0,0 +1,373 @@ +/* + * Copyright 2000-2003 Niels Provos + * 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 + +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "event.h" +#include "event-internal.h" +#include "evsignal.h" +#include "log.h" + +/* due to limitations in the epoll interface, we need to keep track of + * all file descriptors outself. + */ +struct evepoll { + struct event *evread; + struct event *evwrite; +}; + +struct epollop { + struct evepoll *fds; + int nfds; + struct epoll_event *events; + int nevents; + int epfd; +}; + +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 *); + +const struct eventop epollops = { + "epoll", + epoll_init, + epoll_add, + epoll_del, + epoll_dispatch, + epoll_dealloc, + 1 /* need reinit */ +}; + +#ifdef HAVE_SETFD +#define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, 1) == -1) \ + event_warn("fcntl(%d, F_SETFD)", x); \ +} while (0) +#else +#define FD_CLOSEONEXEC(x) +#endif + +#define NEVENT 32000 + +/* On Linux kernels at least up to 2.6.24.4, epoll can't handle timeout + * values bigger than (LONG_MAX - 999ULL)/HZ. HZ in the wild can be + * as big as 1000, and LONG_MAX can be as small as (1<<31)-1, so the + * largest number of msec we can support here is 2147482. Let's + * round that down by 47 seconds. + */ +#define MAX_EPOLL_TIMEOUT_MSEC (35*60*1000) + +static void * +epoll_init(struct event_base *base) +{ + int epfd, nfiles = NEVENT; + struct rlimit rl; + struct epollop *epollop; + + /* Disable epollueue when this environment variable is set */ + if (getenv("EVENT_NOEPOLL")) + return (NULL); + + if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && + rl.rlim_cur != RLIM_INFINITY) { + /* + * Solaris is somewhat retarded - it's important to drop + * backwards compatibility when making changes. So, don't + * dare to put rl.rlim_cur here. + */ + nfiles = rl.rlim_cur - 1; + } + + /* Initalize the kernel queue */ + + if ((epfd = epoll_create(nfiles)) == -1) { + if (errno != ENOSYS) + event_warn("epoll_create"); + return (NULL); + } + + FD_CLOSEONEXEC(epfd); + + if (!(epollop = calloc(1, sizeof(struct epollop)))) + return (NULL); + + epollop->epfd = epfd; + + /* Initalize fields */ + epollop->events = malloc(nfiles * sizeof(struct epoll_event)); + if (epollop->events == NULL) { + free(epollop); + return (NULL); + } + epollop->nevents = nfiles; + + epollop->fds = calloc(nfiles, sizeof(struct evepoll)); + if (epollop->fds == NULL) { + free(epollop->events); + free(epollop); + return (NULL); + } + epollop->nfds = nfiles; + + evsignal_init(base); + + return (epollop); +} + +static int +epoll_recalc(struct event_base *base, void *arg, int max) +{ + struct epollop *epollop = arg; + + if (max >= epollop->nfds) { + struct evepoll *fds; + int nfds; + + nfds = epollop->nfds; + while (nfds <= max) + nfds <<= 1; + + fds = realloc(epollop->fds, nfds * sizeof(struct evepoll)); + if (fds == NULL) { + event_warn("realloc"); + return (-1); + } + epollop->fds = fds; + memset(fds + epollop->nfds, 0, + (nfds - epollop->nfds) * sizeof(struct evepoll)); + epollop->nfds = nfds; + } + + return (0); +} + +static int +epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + struct epollop *epollop = arg; + struct epoll_event *events = epollop->events; + struct evepoll *evep; + int i, res, timeout = -1; + + if (tv != NULL) + timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + + if (timeout > MAX_EPOLL_TIMEOUT_MSEC) { + /* Linux kernels can wait forever if the timeout is too big; + * see comment on MAX_EPOLL_TIMEOUT_MSEC. */ + timeout = MAX_EPOLL_TIMEOUT_MSEC; + } + + res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); + + if (res == -1) { + if (errno != EINTR) { + event_warn("epoll_wait"); + return (-1); + } + + evsignal_process(base); + return (0); + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + event_debug(("%s: epoll_wait reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int what = events[i].events; + struct event *evread = NULL, *evwrite = NULL; + int fd = events[i].data.fd; + + if (fd < 0 || fd >= epollop->nfds) + continue; + evep = &epollop->fds[fd]; + + if (what & (EPOLLHUP|EPOLLERR)) { + evread = evep->evread; + evwrite = evep->evwrite; + } else { + if (what & EPOLLIN) { + evread = evep->evread; + } + + if (what & EPOLLOUT) { + evwrite = evep->evwrite; + } + } + + if (!(evread||evwrite)) + continue; + + if (evread != NULL) + event_active(evread, EV_READ, 1); + if (evwrite != NULL) + event_active(evwrite, EV_WRITE, 1); + } + + return (0); +} + + +static int +epoll_add(void *arg, struct event *ev) +{ + struct epollop *epollop = arg; + struct epoll_event epev = {0, {0}}; + struct evepoll *evep; + int fd, op, events; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(ev)); + + fd = ev->ev_fd; + if (fd >= epollop->nfds) { + /* Extent the file descriptor array as necessary */ + if (epoll_recalc(ev->ev_base, epollop, fd) == -1) + return (-1); + } + evep = &epollop->fds[fd]; + op = EPOLL_CTL_ADD; + events = 0; + if (evep->evread != NULL) { + events |= EPOLLIN; + op = EPOLL_CTL_MOD; + } + if (evep->evwrite != NULL) { + events |= EPOLLOUT; + op = EPOLL_CTL_MOD; + } + + if (ev->ev_events & EV_READ) + events |= EPOLLIN; + if (ev->ev_events & EV_WRITE) + events |= EPOLLOUT; + + epev.data.fd = fd; + epev.events = events; + if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1) + return (-1); + + /* Update events responsible */ + if (ev->ev_events & EV_READ) + evep->evread = ev; + if (ev->ev_events & EV_WRITE) + evep->evwrite = ev; + + return (0); +} + +static int +epoll_del(void *arg, struct event *ev) +{ + struct epollop *epollop = arg; + struct epoll_event epev = {0, {0}}; + struct evepoll *evep; + int fd, events, op; + int needwritedelete = 1, needreaddelete = 1; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(ev)); + + fd = ev->ev_fd; + if (fd >= epollop->nfds) + return (0); + evep = &epollop->fds[fd]; + + op = EPOLL_CTL_DEL; + events = 0; + + if (ev->ev_events & EV_READ) + events |= EPOLLIN; + if (ev->ev_events & EV_WRITE) + events |= EPOLLOUT; + + if ((events & (EPOLLIN|EPOLLOUT)) != (EPOLLIN|EPOLLOUT)) { + if ((events & EPOLLIN) && evep->evwrite != NULL) { + needwritedelete = 0; + events = EPOLLOUT; + op = EPOLL_CTL_MOD; + } else if ((events & EPOLLOUT) && evep->evread != NULL) { + needreaddelete = 0; + events = EPOLLIN; + op = EPOLL_CTL_MOD; + } + } + + epev.events = events; + epev.data.fd = fd; + + if (needreaddelete) + evep->evread = NULL; + if (needwritedelete) + evep->evwrite = NULL; + + if (epoll_ctl(epollop->epfd, op, fd, &epev) == -1) + return (-1); + + return (0); +} + +static void +epoll_dealloc(struct event_base *base, void *arg) +{ + struct epollop *epollop = arg; + + evsignal_dealloc(base); + if (epollop->fds) + free(epollop->fds); + if (epollop->events) + free(epollop->events); + if (epollop->epfd >= 0) + close(epollop->epfd); + + memset(epollop, 0, sizeof(struct epollop)); + free(epollop); +} diff --git a/libevent/epoll_sub.c b/libevent/epoll_sub.c new file mode 100644 index 00000000000..431970c73a6 --- /dev/null +++ b/libevent/epoll_sub.c @@ -0,0 +1,52 @@ +/* + * Copyright 2003 Niels Provos + * 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. + */ +#include + +#include +#include +#include +#include +#include + +int +epoll_create(int size) +{ + return (syscall(__NR_epoll_create, size)); +} + +int +epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + + return (syscall(__NR_epoll_ctl, epfd, op, fd, event)); +} + +int +epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) +{ + return (syscall(__NR_epoll_wait, epfd, events, maxevents, timeout)); +} diff --git a/libevent/evbuffer.c b/libevent/evbuffer.c new file mode 100644 index 00000000000..f2179a5044f --- /dev/null +++ b/libevent/evbuffer.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2002-2004 Niels Provos + * 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. + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#endif + +#ifdef WIN32 +#include +#endif + +#include "evutil.h" +#include "event.h" + +/* prototypes */ + +void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); + +static int +bufferevent_add(struct event *ev, int timeout) +{ + struct timeval tv, *ptv = NULL; + + if (timeout) { + evutil_timerclear(&tv); + tv.tv_sec = timeout; + ptv = &tv; + } + + return (event_add(ev, ptv)); +} + +/* + * This callback is executed when the size of the input buffer changes. + * We use it to apply back pressure on the reading side. + */ + +void +bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now, + void *arg) { + struct bufferevent *bufev = arg; + /* + * If we are below the watermark then reschedule reading if it's + * still enabled. + */ + if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) { + evbuffer_setcb(buf, NULL, NULL); + + if (bufev->enabled & EV_READ) + bufferevent_add(&bufev->ev_read, bufev->timeout_read); + } +} + +static void +bufferevent_readcb(int fd, short event, void *arg) +{ + struct bufferevent *bufev = arg; + int res = 0; + short what = EVBUFFER_READ; + size_t len; + int howmuch = -1; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto error; + } + + /* + * If we have a high watermark configured then we don't want to + * read more data than would make us reach the watermark. + */ + if (bufev->wm_read.high != 0) { + howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input); + /* we might have lowered the watermark, stop reading */ + if (howmuch <= 0) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + evbuffer_setcb(buf, + bufferevent_read_pressure_cb, bufev); + return; + } + } + + res = evbuffer_read(bufev->input, fd, howmuch); + if (res == -1) { + if (errno == EAGAIN || errno == EINTR) + goto reschedule; + /* error case */ + what |= EVBUFFER_ERROR; + } else if (res == 0) { + /* eof case */ + what |= EVBUFFER_EOF; + } + + if (res <= 0) + goto error; + + bufferevent_add(&bufev->ev_read, bufev->timeout_read); + + /* See if this callbacks meets the water marks */ + len = EVBUFFER_LENGTH(bufev->input); + if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) + return; + if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + + /* Now schedule a callback for us when the buffer changes */ + evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); + } + + /* Invoke the user callback - must always be called last */ + if (bufev->readcb != NULL) + (*bufev->readcb)(bufev, bufev->cbarg); + return; + + reschedule: + bufferevent_add(&bufev->ev_read, bufev->timeout_read); + return; + + error: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +static void +bufferevent_writecb(int fd, short event, void *arg) +{ + struct bufferevent *bufev = arg; + int res = 0; + short what = EVBUFFER_WRITE; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto error; + } + + if (EVBUFFER_LENGTH(bufev->output)) { + res = evbuffer_write(bufev->output, fd); + if (res == -1) { +#ifndef WIN32 +/*todo. evbuffer uses WriteFile when WIN32 is set. WIN32 system calls do not + *set errno. thus this error checking is not portable*/ + if (errno == EAGAIN || + errno == EINTR || + errno == EINPROGRESS) + goto reschedule; + /* error case */ + what |= EVBUFFER_ERROR; + +#else + goto reschedule; +#endif + + } else if (res == 0) { + /* eof case */ + what |= EVBUFFER_EOF; + } + if (res <= 0) + goto error; + } + + if (EVBUFFER_LENGTH(bufev->output) != 0) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + + /* + * Invoke the user callback if our buffer is drained or below the + * low watermark. + */ + if (bufev->writecb != NULL && + EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) + (*bufev->writecb)(bufev, bufev->cbarg); + + return; + + reschedule: + if (EVBUFFER_LENGTH(bufev->output) != 0) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + return; + + error: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +/* + * Create a new buffered event object. + * + * The read callback is invoked whenever we read new data. + * The write callback is invoked whenever the output buffer is drained. + * The error callback is invoked on a write/read error or on EOF. + * + * Both read and write callbacks maybe NULL. The error callback is not + * allowed to be NULL and have to be provided always. + */ + +struct bufferevent * +bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb, + everrorcb errorcb, void *cbarg) +{ + struct bufferevent *bufev; + + if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL) + return (NULL); + + if ((bufev->input = evbuffer_new()) == NULL) { + free(bufev); + return (NULL); + } + + if ((bufev->output = evbuffer_new()) == NULL) { + evbuffer_free(bufev->input); + free(bufev); + return (NULL); + } + + event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev); + event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev); + + bufferevent_setcb(bufev, readcb, writecb, errorcb, cbarg); + + /* + * Set to EV_WRITE so that using bufferevent_write is going to + * trigger a callback. Reading needs to be explicitly enabled + * because otherwise no data will be available. + */ + bufev->enabled = EV_WRITE; + + return (bufev); +} + +void +bufferevent_setcb(struct bufferevent *bufev, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg) +{ + bufev->readcb = readcb; + bufev->writecb = writecb; + bufev->errorcb = errorcb; + + bufev->cbarg = cbarg; +} + +void +bufferevent_setfd(struct bufferevent *bufev, int fd) +{ + event_del(&bufev->ev_read); + event_del(&bufev->ev_write); + + event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev); + event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev); + if (bufev->ev_base != NULL) { + event_base_set(bufev->ev_base, &bufev->ev_read); + event_base_set(bufev->ev_base, &bufev->ev_write); + } + + /* might have to manually trigger event registration */ +} + +int +bufferevent_priority_set(struct bufferevent *bufev, int priority) +{ + if (event_priority_set(&bufev->ev_read, priority) == -1) + return (-1); + if (event_priority_set(&bufev->ev_write, priority) == -1) + return (-1); + + return (0); +} + +/* Closing the file descriptor is the responsibility of the caller */ + +void +bufferevent_free(struct bufferevent *bufev) +{ + event_del(&bufev->ev_read); + event_del(&bufev->ev_write); + + evbuffer_free(bufev->input); + evbuffer_free(bufev->output); + + free(bufev); +} + +/* + * Returns 0 on success; + * -1 on failure. + */ + +int +bufferevent_write(struct bufferevent *bufev, const void *data, size_t size) +{ + int res; + + res = evbuffer_add(bufev->output, data, size); + + if (res == -1) + return (res); + + /* If everything is okay, we need to schedule a write */ + if (size > 0 && (bufev->enabled & EV_WRITE)) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + + return (res); +} + +int +bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf) +{ + int res; + + res = bufferevent_write(bufev, buf->buffer, buf->off); + if (res != -1) + evbuffer_drain(buf, buf->off); + + return (res); +} + +size_t +bufferevent_read(struct bufferevent *bufev, void *data, size_t size) +{ + struct evbuffer *buf = bufev->input; + + if (buf->off < size) + size = buf->off; + + /* Copy the available data to the user buffer */ + memcpy(data, buf->buffer, size); + + if (size) + evbuffer_drain(buf, size); + + return (size); +} + +int +bufferevent_enable(struct bufferevent *bufev, short event) +{ + if (event & EV_READ) { + if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1) + return (-1); + } + if (event & EV_WRITE) { + if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1) + return (-1); + } + + bufev->enabled |= event; + return (0); +} + +int +bufferevent_disable(struct bufferevent *bufev, short event) +{ + if (event & EV_READ) { + if (event_del(&bufev->ev_read) == -1) + return (-1); + } + if (event & EV_WRITE) { + if (event_del(&bufev->ev_write) == -1) + return (-1); + } + + bufev->enabled &= ~event; + return (0); +} + +/* + * Sets the read and write timeout for a buffered event. + */ + +void +bufferevent_settimeout(struct bufferevent *bufev, + int timeout_read, int timeout_write) { + bufev->timeout_read = timeout_read; + bufev->timeout_write = timeout_write; + + if (event_pending(&bufev->ev_read, EV_READ, NULL)) + bufferevent_add(&bufev->ev_read, timeout_read); + if (event_pending(&bufev->ev_write, EV_WRITE, NULL)) + bufferevent_add(&bufev->ev_write, timeout_write); +} + +/* + * Sets the water marks + */ + +void +bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark) +{ + if (events & EV_READ) { + bufev->wm_read.low = lowmark; + bufev->wm_read.high = highmark; + } + + if (events & EV_WRITE) { + bufev->wm_write.low = lowmark; + bufev->wm_write.high = highmark; + } + + /* If the watermarks changed then see if we should call read again */ + bufferevent_read_pressure_cb(bufev->input, + 0, EVBUFFER_LENGTH(bufev->input), bufev); +} + +int +bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) +{ + int res; + + bufev->ev_base = base; + + res = event_base_set(base, &bufev->ev_read); + if (res == -1) + return (res); + + res = event_base_set(base, &bufev->ev_write); + return (res); +} diff --git a/libevent/evdns.3 b/libevent/evdns.3 new file mode 100644 index 00000000000..10414fa2efb --- /dev/null +++ b/libevent/evdns.3 @@ -0,0 +1,322 @@ +.\" +.\" Copyright (c) 2006 Niels Provos +.\" 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 ``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. +.\" +.Dd October 7, 2006 +.Dt EVDNS 3 +.Os +.Sh NAME +.Nm evdns_init +.Nm evdns_shutdown +.Nm evdns_err_to_string +.Nm evdns_nameserver_add +.Nm evdns_count_nameservers +.Nm evdns_clear_nameservers_and_suspend +.Nm evdns_resume +.Nm evdns_nameserver_ip_add +.Nm evdns_resolve_ipv4 +.Nm evdns_resolve_reverse +.Nm evdns_resolv_conf_parse +.Nm evdns_config_windows_nameservers +.Nm evdns_search_clear +.Nm evdns_search_add +.Nm evdns_search_ndots_set +.Nm evdns_set_log_fn +.Nd asynchronous functions for DNS resolution. +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Fd #include +.Ft int +.Fn evdns_init +.Ft void +.Fn evdns_shutdown "int fail_requests" +.Ft "const char *" +.Fn evdns_err_to_string "int err" +.Ft int +.Fn evdns_nameserver_add "unsigned long int address" +.Ft int +.Fn evdns_count_nameservers +.Ft int +.Fn evdns_clear_nameservers_and_suspend +.Ft int +.Fn evdns_resume +.Ft int +.Fn evdns_nameserver_ip_add(const char *ip_as_string); +.Ft int +.Fn evdns_resolve_ipv4 "const char *name" "int flags" "evdns_callback_type callback" "void *ptr" +.Ft int +.Fn evdns_resolve_reverse "struct in_addr *in" "int flags" "evdns_callback_type callback" "void *ptr" +.Ft int +.Fn evdns_resolv_conf_parse "int flags" "const char *" +.Ft void +.Fn evdns_search_clear +.Ft void +.Fn evdns_search_add "const char *domain" +.Ft void +.Fn evdns_search_ndots_set "const int ndots" +.Ft void +.Fn evdns_set_log_fn "evdns_debug_log_fn_type fn" +.Ft int +.Fn evdns_config_windows_nameservers +.Sh DESCRIPTION +Welcome, gentle reader +.Pp +Async DNS lookups are really a whole lot harder than they should be, +mostly stemming from the fact that the libc resolver has never been +very good at them. Before you use this library you should see if libc +can do the job for you with the modern async call getaddrinfo_a +(see http://www.imperialviolet.org/page25.html#e498). Otherwise, +please continue. +.Pp +This code is based on libevent and you must call event_init before +any of the APIs in this file. You must also seed the OpenSSL random +source if you are using OpenSSL for ids (see below). +.Pp +This library is designed to be included and shipped with your source +code. You statically link with it. You should also test for the +existence of strtok_r and define HAVE_STRTOK_R if you have it. +.Pp +The DNS protocol requires a good source of id numbers and these +numbers should be unpredictable for spoofing reasons. There are +three methods for generating them here and you must define exactly +one of them. In increasing order of preference: +.Pp +.Bl -tag -width "DNS_USE_GETTIMEOFDAY_FOR_ID" -compact -offset indent +.It DNS_USE_GETTIMEOFDAY_FOR_ID +Using the bottom 16 bits of the usec result from gettimeofday. This +is a pretty poor solution but should work anywhere. +.It DNS_USE_CPU_CLOCK_FOR_ID +Using the bottom 16 bits of the nsec result from the CPU's time +counter. This is better, but may not work everywhere. Requires +POSIX realtime support and you'll need to link against -lrt on +glibc systems at least. +.It DNS_USE_OPENSSL_FOR_ID +Uses the OpenSSL RAND_bytes call to generate the data. You must +have seeded the pool before making any calls to this library. +.El +.Pp +The library keeps track of the state of nameservers and will avoid +them when they go down. Otherwise it will round robin between them. +.Pp +Quick start guide: + #include "evdns.h" + void callback(int result, char type, int count, int ttl, + void *addresses, void *arg); + evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + evdns_resolve("www.hostname.com", 0, callback, NULL); +.Pp +When the lookup is complete the callback function is called. The +first argument will be one of the DNS_ERR_* defines in evdns.h. +Hopefully it will be DNS_ERR_NONE, in which case type will be +DNS_IPv4_A, count will be the number of IP addresses, ttl is the time +which the data can be cached for (in seconds), addresses will point +to an array of uint32_t's and arg will be whatever you passed to +evdns_resolve. +.Pp +Searching: +.Pp +In order for this library to be a good replacement for glibc's resolver it +supports searching. This involves setting a list of default domains, in +which names will be queried for. The number of dots in the query name +determines the order in which this list is used. +.Pp +Searching appears to be a single lookup from the point of view of the API, +although many DNS queries may be generated from a single call to +evdns_resolve. Searching can also drastically slow down the resolution +of names. +.Pp +To disable searching: +.Bl -enum -compact -offset indent +.It +Never set it up. If you never call +.Fn evdns_resolv_conf_parse, +.Fn evdns_init, +or +.Fn evdns_search_add +then no searching will occur. +.It +If you do call +.Fn evdns_resolv_conf_parse +then don't pass +.Va DNS_OPTION_SEARCH +(or +.Va DNS_OPTIONS_ALL, +which implies it). +.It +When calling +.Fn evdns_resolve, +pass the +.Va DNS_QUERY_NO_SEARCH +flag. +.El +.Pp +The order of searches depends on the number of dots in the name. If the +number is greater than the ndots setting then the names is first tried +globally. Otherwise each search domain is appended in turn. +.Pp +The ndots setting can either be set from a resolv.conf, or by calling +evdns_search_ndots_set. +.Pp +For example, with ndots set to 1 (the default) and a search domain list of +["myhome.net"]: + Query: www + Order: www.myhome.net, www. +.Pp + Query: www.abc + Order: www.abc., www.abc.myhome.net +.Pp +.Sh API reference +.Pp +.Bl -tag -width 0123456 +.It Ft int Fn evdns_init +Initializes support for non-blocking name resolution by calling +.Fn evdns_resolv_conf_parse +on UNIX and +.Fn evdns_config_windows_nameservers +on Windows. +.It Ft int Fn 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. +.It Ft int Fn 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 +.It Ft int Fn 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). +.Pp +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. +.Pp +Returns non-zero on error +.It Ft void Fn evdns_search_clear +Clears the list of search domains +.It Ft void Fn evdns_search_add "const char *domain" +Add a domain to the list of search domains +.It Ft void Fn 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. +.It Ft int Fn 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. +.It Ft int Fn 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. +.It Ft int Fn evdns_resume "void" +Re-attempt resolves left in limbo after an earlier call to +evdns_clear_nameservers_and_suspend(). +.It Ft int Fn 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. +.It Ft int Fn evdns_resolv_conf_parse "int flags" "const char *filename" +Parse a resolv.conf like file from the given filename. +.Pp +See the man page for resolv.conf for the format of this file. +The flags argument determines what information is parsed from +this file: +.Bl -tag -width "DNS_OPTION_NAMESERVERS" -offset indent -compact -nested +.It DNS_OPTION_SEARCH +domain, search and ndots options +.It DNS_OPTION_NAMESERVERS +nameserver lines +.It DNS_OPTION_MISC +timeout and attempts options +.It DNS_OPTIONS_ALL +all of the above +.El +.Pp +The following directives are not parsed from the file: + sortlist, rotate, no-check-names, inet6, debug +.Pp +Returns non-zero on error: +.Bl -tag -width "0" -offset indent -compact -nested +.It 0 +no errors +.It 1 +failed to open file +.It 2 +failed to stat file +.It 3 +file too large +.It 4 +out of memory +.It 5 +short read from file +.El +.El +.Sh Internals: +Requests are kept in two queues. The first is the inflight queue. In +this queue requests have an allocated transaction id and nameserver. +They will soon be transmitted if they haven't already been. +.Pp +The second is the waiting queue. The size of the inflight ring is +limited and all other requests wait in waiting queue for space. This +bounds the number of concurrent requests so that we don't flood the +nameserver. Several algorithms require a full walk of the inflight +queue and so bounding its size keeps thing going nicely under huge +(many thousands of requests) loads. +.Pp +If a nameserver loses too many requests it is considered down and we +try not to use it. After a while we send a probe to that nameserver +(a lookup for google.com) and, if it replies, we consider it working +again. If the nameserver fails a probe we wait longer to try again +with the next probe. +.Sh SEE ALSO +.Xr event 3 , +.Xr gethostbyname 3 , +.Xr resolv.conf 5 +.Sh HISTORY +The +.Nm evdns +API was developed by Adam Langley on top of the +.Nm libevent +API. +The code was integrate into +.Nm Tor +by Nick Mathewson and finally put into +.Nm libevent +itself by Niels Provos. +.Sh AUTHORS +The +.Nm evdns +API and code was written by Adam Langley with significant +contributions by Nick Mathewson. +.Sh BUGS +This documentation is neither complete nor authoritative. +If you are in doubt about the usage of this API then +check the source code to find out how it works, write +up the missing piece of documentation and send it to +me for inclusion in this man page. diff --git a/libevent/evdns.c b/libevent/evdns.c new file mode 100644 index 00000000000..e13357f1596 --- /dev/null +++ b/libevent/evdns.c @@ -0,0 +1,3200 @@ +/* $Id: evdns.c 6979 2006-08-04 18:31:13Z nickm $ */ + +/* The original version of this module was written by Adam Langley; for + * a history of modifications, check out the subversion logs. + * + * When editing this module, try to keep it re-mergeable by Adam. Don't + * reformat the whitespace, add Tor dependencies, or so on. + * + * TODO: + * - Support IPv6 and PTR records. + * - Replace all externally visible magic numbers with #defined constants. + * - Write doccumentation for APIs of all external functions. + */ + +/* Async DNS Library + * Adam Langley + * http://www.imperialviolet.org/eventdns.html + * Public Domain code + * + * This software is Public Domain. To view a copy of the public domain dedication, + * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + * + * I ask and expect, but do not require, that all derivative works contain an + * attribution similar to: + * Parts developed by Adam Langley + * + * You may wish to replace the word "Parts" with something else depending on + * the amount of original code. + * + * (Derivative works does not include programs which link against, run or include + * the source verbatim in their source distributions) + * + * Version: 0.1b + */ + +#include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DNS_USE_FTIME_FOR_ID +#include +#endif + +#ifndef DNS_USE_CPU_CLOCK_FOR_ID +#ifdef HAVE_GETTIMEOFDAY +#define DNS_USE_GETTIMEOFDAY_FOR_ID 1 +#endif +#endif + +#ifndef DNS_USE_CPU_CLOCK_FOR_ID +#ifdef HAVE_GETTIMEOFDAY +#define DNS_USE_GETTIMEOFDAY_FOR_ID 1 +#endif +#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 + +#ifdef DNS_USE_CPU_CLOCK_FOR_ID +#ifdef DNS_USE_OPENSSL_FOR_ID +#error Multiple id options selected +#endif +#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID +#error Multiple id options selected +#endif +#include +#endif + +#ifdef DNS_USE_OPENSSL_FOR_ID +#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID +#error Multiple id options selected +#endif +#include +#endif + +#ifndef _FORTIFY_SOURCE +#define _FORTIFY_SOURCE 3 +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#include "evdns.h" +#include "evutil.h" +#include "log.h" +#ifdef WIN32 +#include +#include +#include +#include +#else +#include +#include +#include +#endif + +#ifdef HAVE_NETINET_IN6_H +#include +#endif + +#define EVDNS_LOG_DEBUG 0 +#define EVDNS_LOG_WARN 1 + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#include + +#undef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) + +#ifdef __USE_ISOC99B +/* libevent doesn't work without this */ +typedef ev_uint8_t u_char; +typedef unsigned int uint; +#endif +#include + +#define u64 ev_uint64_t +#define u32 ev_uint32_t +#define u16 ev_uint16_t +#define u8 ev_uint8_t + +#ifdef WIN32 +#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 +#define TYPE_CNAME 5 +#define TYPE_PTR EVDNS_TYPE_PTR +#define TYPE_AAAA EVDNS_TYPE_AAAA + +#define CLASS_INET EVDNS_CLASS_INET + +struct request { + u8 *request; /* the dns packet data */ + unsigned int request_len; + int reissue_count; + int tx_count; /* the number of times that this packet has been sent */ + unsigned int request_type; /* TYPE_PTR or TYPE_A */ + void *user_pointer; /* the pointer given to us for this request */ + evdns_callback_type user_callback; + struct nameserver *ns; /* the server which we last sent it */ + + /* elements used by the searching code */ + int search_index; + struct search_state *search_state; + char *search_origname; /* needs to be free()ed */ + int search_flags; + + /* these objects are kept in a circular list */ + struct request *next, *prev; + + struct event timeout_event; + + u16 trans_id; /* the transaction id */ + char request_appended; /* true if the request pointer is data which follows this struct */ + char transmit_me; /* needs to be transmitted */ +}; + +#ifndef HAVE_STRUCT_IN6_ADDR +struct in6_addr { + u8 s6_addr[16]; +}; +#endif + +struct reply { + unsigned int type; + unsigned int have_answer; + union { + struct { + u32 addrcount; + u32 addresses[MAX_ADDRS]; + } a; + struct { + u32 addrcount; + struct in6_addr addresses[MAX_ADDRS]; + } aaaa; + struct { + char name[HOST_NAME_MAX]; + } ptr; + } data; +}; + +struct nameserver { + int socket; /* a connected UDP socket */ + u32 address; + u16 port; + int failed_times; /* number of times which we have given this server a chance */ + int timedout; /* number of times in a row a request has timed out */ + struct event event; + /* these objects are kept in a circular list */ + struct nameserver *next, *prev; + struct event timeout_event; /* used to keep the timeout for */ + /* when we next probe this server. */ + /* Valid if state == 0 */ + char state; /* zero if we think that this server is down */ + char choked; /* true if we have an EAGAIN from this server's socket */ + char write_waiting; /* true if we are waiting for EV_WRITE events */ +}; + +static struct request *req_head = NULL, *req_waiting_head = NULL; +static struct nameserver *server_head = NULL; + +/* Represents a local port where we're listening for DNS requests. Right now, */ +/* only UDP is supported. */ +struct evdns_server_port { + int socket; /* socket we use to read queries and write replies. */ + int refcnt; /* reference count. */ + char choked; /* Are we currently blocked from writing? */ + char closing; /* Are we trying to close this port, pending writes? */ + evdns_request_callback_fn_type user_callback; /* Fn to handle requests */ + void *user_data; /* Opaque pointer passed to user_callback */ + struct event event; /* Read/write event */ + /* circular list of replies that we want to write. */ + struct server_request *pending_replies; +}; + +/* Represents part of a reply being built. (That is, a single RR.) */ +struct server_reply_item { + struct server_reply_item *next; /* next item in sequence. */ + char *name; /* name part of the RR */ + u16 type : 16; /* The RR type */ + u16 class : 16; /* The RR class (usually CLASS_INET) */ + u32 ttl; /* The RR TTL */ + char is_name; /* True iff data is a label */ + u16 datalen; /* Length of data; -1 if data is a label */ + void *data; /* The contents of the RR */ +}; + +/* Represents a request that we've received as a DNS server, and holds */ +/* the components of the reply as we're constructing it. */ +struct server_request { + /* Pointers to the next and previous entries on the list of replies */ + /* that we're waiting to write. Only set if we have tried to respond */ + /* and gotten EAGAIN. */ + struct server_request *next_pending; + struct server_request *prev_pending; + + u16 trans_id; /* Transaction id. */ + struct evdns_server_port *port; /* Which port received this request on? */ + struct sockaddr_storage addr; /* Where to send the response */ + socklen_t addrlen; /* length of addr */ + + int n_answer; /* how many answer RRs have been set? */ + int n_authority; /* how many authority RRs have been set? */ + int n_additional; /* how many additional RRs have been set? */ + + struct server_reply_item *answer; /* linked list of answer RRs */ + struct server_reply_item *authority; /* linked list of authority RRs */ + struct server_reply_item *additional; /* linked list of additional RRs */ + + /* Constructed response. Only set once we're ready to send a reply. */ + /* Once this is set, the RR fields are cleared, and no more should be set. */ + char *response; + size_t response_len; + + /* Caller-visible fields: flags, questions. */ + struct evdns_server_request base; +}; + +/* helper macro */ +#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0)) + +/* Given a pointer to an evdns_server_request, get the corresponding */ +/* server_request. */ +#define TO_SERVER_REQUEST(base_ptr) \ + ((struct server_request*) \ + (((char*)(base_ptr) - OFFSET_OF(struct server_request, base)))) + +/* The number of good nameservers that we have */ +static int global_good_nameservers = 0; + +/* inflight requests are contained in the req_head list */ +/* and are actually going out across the network */ +static int global_requests_inflight = 0; +/* requests which aren't inflight are in the waiting list */ +/* and are counted here */ +static int global_requests_waiting = 0; + +static int global_max_requests_inflight = 64; + +static struct timeval global_timeout = {5, 0}; /* 5 seconds */ +static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ +static int global_max_retransmits = 3; /* number of times we'll retransmit a request which timed out */ +/* number of timeouts in a row before we consider this server to be down */ +static int global_max_nameserver_timeout = 3; + +/* These are the timeout values for nameservers. If we find a nameserver is down */ +/* we try to probe it at intervals as given below. Values are in seconds. */ +static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}}; +static const int global_nameserver_timeouts_length = sizeof(global_nameserver_timeouts)/sizeof(struct timeval); + +static struct nameserver *nameserver_pick(void); +static void evdns_request_insert(struct request *req, struct request **head); +static void nameserver_ready_callback(int fd, short events, void *arg); +static int evdns_transmit(void); +static int evdns_request_transmit(struct request *req); +static void nameserver_send_probe(struct nameserver *const ns); +static void search_request_finished(struct request *const); +static int search_try_next(struct request *const req); +static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg); +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 *const req); + +static int server_request_free(struct server_request *req); +static void server_request_free_answers(struct server_request *req); +static void server_port_free(struct evdns_server_port *port); +static void server_port_ready_callback(int fd, short events, void *arg); + +static int strtoint(const char *const str); + +#ifdef WIN32 +static int +last_error(int sock) +{ + int optval, optvallen=sizeof(optval); + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK && sock >= 0) { + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval, + &optvallen)) + return err; + if (optval) + return optval; + } + return err; + +} +static int +error_is_eagain(int err) +{ + return err == EAGAIN || err == WSAEWOULDBLOCK; +} +static int +inet_aton(const char *c, struct in_addr *addr) +{ + ev_uint32_t r; + if (strcmp(c, "255.255.255.255") == 0) { + addr->s_addr = 0xffffffffu; + } else { + r = inet_addr(c); + if (r == INADDR_NONE) + return 0; + addr->s_addr = r; + } + return 1; +} +#else +#define last_error(sock) (errno) +#define error_is_eagain(err) ((err) == EAGAIN) +#endif +#define CLOSE_SOCKET(s) EVUTIL_CLOSESOCKET(s) + +#define ISSPACE(c) isspace((int)(unsigned char)(c)) +#define ISDIGIT(c) isdigit((int)(unsigned char)(c)) + +static const char * +debug_ntoa(u32 address) +{ + static char buf[32]; + u32 a = ntohl(address); + evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", + (int)(u8)((a>>24)&0xff), + (int)(u8)((a>>16)&0xff), + (int)(u8)((a>>8 )&0xff), + (int)(u8)((a )&0xff)); + return buf; +} + +static evdns_debug_log_fn_type evdns_log_fn = NULL; + +void +evdns_set_log_fn(evdns_debug_log_fn_type fn) +{ + evdns_log_fn = fn; +} + +#ifdef __GNUC__ +#define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3))) +#else +#define EVDNS_LOG_CHECK +#endif + +static void _evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK; +static void +_evdns_log(int warn, const char *fmt, ...) +{ + va_list args; + static char buf[512]; + if (!evdns_log_fn) + return; + va_start(args,fmt); + evutil_vsnprintf(buf, sizeof(buf), fmt, args); + buf[sizeof(buf)-1] = '\0'; + evdns_log_fn(warn, buf); + va_end(args); +} + +#define log _evdns_log + +/* This walks the list of inflight requests to find the */ +/* one with a matching transaction id. Returns NULL on */ +/* failure */ +static struct request * +request_find_from_trans_id(u16 trans_id) { + struct request *req = req_head, *const started_at = req_head; + + if (req) { + do { + if (req->trans_id == trans_id) return req; + req = req->next; + } while (req != started_at); + } + + return NULL; +} + +/* a libevent callback function which is called when a nameserver */ +/* has gone down and we want to test if it has came back to life yet */ +static void +nameserver_prod_callback(int fd, short events, void *arg) { + struct nameserver *const ns = (struct nameserver *) arg; + (void)fd; + (void)events; + + nameserver_send_probe(ns); +} + +/* a libevent callback which is called when a nameserver probe (to see if */ +/* it has come back to life) times out. We increment the count of failed_times */ +/* and wait longer to send the next probe packet. */ +static void +nameserver_probe_failed(struct nameserver *const ns) { + const struct timeval * timeout; + (void) evtimer_del(&ns->timeout_event); + if (ns->state == 1) { + /* This can happen if the nameserver acts in a way which makes us mark */ + /* it as bad and then starts sending good replies. */ + return; + } + + timeout = + &global_nameserver_timeouts[MIN(ns->failed_times, + global_nameserver_timeouts_length - 1)]; + ns->failed_times++; + + if (evtimer_add(&ns->timeout_event, (struct timeval *) timeout) < 0) { + log(EVDNS_LOG_WARN, + "Error from libevent when adding timer event for %s", + debug_ntoa(ns->address)); + /* ???? Do more? */ + } +} + +/* called when a nameserver has been deemed to have failed. For example, too */ +/* many packets have timed out etc */ +static void +nameserver_failed(struct nameserver *const ns, const char *msg) { + struct request *req, *started_at; + /* if this nameserver has already been marked as failed */ + /* then don't do anything */ + if (!ns->state) return; + + log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s", + debug_ntoa(ns->address), msg); + global_good_nameservers--; + assert(global_good_nameservers >= 0); + if (global_good_nameservers == 0) { + log(EVDNS_LOG_WARN, "All nameservers have failed"); + } + + ns->state = 0; + ns->failed_times = 1; + + if (evtimer_add(&ns->timeout_event, (struct timeval *) &global_nameserver_timeouts[0]) < 0) { + log(EVDNS_LOG_WARN, + "Error from libevent when adding timer event for %s", + debug_ntoa(ns->address)); + /* ???? Do more? */ + } + + /* walk the list of inflight requests to see if any can be reassigned to */ + /* a different server. Requests in the waiting queue don't have a */ + /* nameserver assigned yet */ + + /* if we don't have *any* good nameservers then there's no point */ + /* trying to reassign requests to one */ + if (!global_good_nameservers) return; + + req = req_head; + started_at = req_head; + if (req) { + do { + if (req->tx_count == 0 && req->ns == ns) { + /* still waiting to go out, can be moved */ + /* to another server */ + req->ns = nameserver_pick(); + } + req = req->next; + } while (req != started_at); + } +} + +static void +nameserver_up(struct nameserver *const ns) { + if (ns->state) return; + log(EVDNS_LOG_WARN, "Nameserver %s is back up", + debug_ntoa(ns->address)); + evtimer_del(&ns->timeout_event); + ns->state = 1; + ns->failed_times = 0; + ns->timedout = 0; + global_good_nameservers++; +} + +static void +request_trans_id_set(struct request *const req, const u16 trans_id) { + req->trans_id = trans_id; + *((u16 *) req->request) = htons(trans_id); +} + +/* Called to remove a request from a list and dealloc it. */ +/* head is a pointer to the head of the list it should be */ +/* removed from or NULL if the request isn't in a list. */ +static void +request_finished(struct request *const req, struct request **head) { + if (head) { + if (req->next == req) { + /* only item in the list */ + *head = NULL; + } else { + req->next->prev = req->prev; + req->prev->next = req->next; + if (*head == req) *head = req->next; + } + } + + log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx", + (unsigned long) req); + evtimer_del(&req->timeout_event); + + search_request_finished(req); + global_requests_inflight--; + + if (!req->request_appended) { + /* need to free the request data on it's own */ + free(req->request); + } else { + /* the request data is appended onto the header */ + /* so everything gets free()ed when we: */ + } + + free(req); + + evdns_requests_pump_waiting_queue(); +} + +/* This is called when a server returns a funny error code. */ +/* We try the request again with another server. */ +/* */ +/* return: */ +/* 0 ok */ +/* 1 failed/reissue is pointless */ +static int +request_reissue(struct request *req) { + const struct nameserver *const last_ns = req->ns; + /* the last nameserver should have been marked as failing */ + /* by the caller of this function, therefore pick will try */ + /* not to return it */ + req->ns = nameserver_pick(); + if (req->ns == last_ns) { + /* ... but pick did return it */ + /* not a lot of point in trying again with the */ + /* same server */ + return 1; + } + + req->reissue_count++; + req->tx_count = 0; + req->transmit_me = 1; + + return 0; +} + +/* this function looks for space on the inflight queue and promotes */ +/* requests from the waiting queue if it can. */ +static void +evdns_requests_pump_waiting_queue(void) { + while (global_requests_inflight < global_max_requests_inflight && + global_requests_waiting) { + struct request *req; + /* move a request from the waiting queue to the inflight queue */ + assert(req_waiting_head); + if (req_waiting_head->next == req_waiting_head) { + /* only one item in the queue */ + req = req_waiting_head; + req_waiting_head = NULL; + } else { + req = req_waiting_head; + req->next->prev = req->prev; + req->prev->next = req->next; + req_waiting_head = req->next; + } + + global_requests_waiting--; + global_requests_inflight++; + + req->ns = nameserver_pick(); + request_trans_id_set(req, transaction_id_pick()); + + evdns_request_insert(req, &req_head); + evdns_request_transmit(req); + evdns_transmit(); + } +} + +static void +reply_callback(struct request *const req, u32 ttl, u32 err, struct reply *reply) { + switch (req->request_type) { + case TYPE_A: + if (reply) + req->user_callback(DNS_ERR_NONE, DNS_IPv4_A, + reply->data.a.addrcount, ttl, + reply->data.a.addresses, + req->user_pointer); + else + req->user_callback(err, 0, 0, 0, NULL, req->user_pointer); + return; + case TYPE_PTR: + if (reply) { + char *name = reply->data.ptr.name; + req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl, + &name, req->user_pointer); + } else { + req->user_callback(err, 0, 0, 0, NULL, + req->user_pointer); + } + return; + case TYPE_AAAA: + if (reply) + req->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA, + reply->data.aaaa.addrcount, ttl, + reply->data.aaaa.addresses, + req->user_pointer); + else + req->user_callback(err, 0, 0, 0, NULL, req->user_pointer); + return; + } + assert(0); +} + +/* this processes a parsed reply packet */ +static void +reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) { + int error; + static const int error_codes[] = { + DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, + DNS_ERR_NOTIMPL, DNS_ERR_REFUSED + }; + + if (flags & 0x020f || !reply || !reply->have_answer) { + /* there was an error */ + if (flags & 0x0200) { + error = DNS_ERR_TRUNCATED; + } else { + u16 error_code = (flags & 0x000f) - 1; + if (error_code > 4) { + error = DNS_ERR_UNKNOWN; + } else { + error = error_codes[error_code]; + } + } + + switch(error) { + case DNS_ERR_NOTIMPL: + case DNS_ERR_REFUSED: + /* we regard these errors as marking a bad nameserver */ + if (req->reissue_count < global_max_reissues) { + char msg[64]; + evutil_snprintf(msg, sizeof(msg), + "Bad response %d (%s)", + error, evdns_err_to_string(error)); + nameserver_failed(req->ns, msg); + if (!request_reissue(req)) return; + } + break; + case DNS_ERR_SERVERFAILED: + /* rcode 2 (servfailed) sometimes means "we + * are broken" and sometimes (with some binds) + * means "that request was very confusing." + * Treat this as a timeout, not a failure. + */ + log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; " + "will allow the request to time out.", + debug_ntoa(req->ns->address)); + break; + default: + /* we got a good reply from the nameserver */ + nameserver_up(req->ns); + } + + if (req->search_state && req->request_type != TYPE_PTR) { + /* if we have a list of domains to search in, + * try the next one */ + if (!search_try_next(req)) { + /* a new request was issued so this + * request is finished and */ + /* the user callback will be made when + * that request (or a */ + /* child of it) finishes. */ + request_finished(req, &req_head); + return; + } + } + + /* all else failed. Pass the failure up */ + reply_callback(req, 0, error, NULL); + request_finished(req, &req_head); + } else { + /* all ok, tell the user */ + reply_callback(req, ttl, 0, reply); + nameserver_up(req->ns); + request_finished(req, &req_head); + } +} + +static int +name_parse(u8 *packet, int length, int *idx, char *name_out, int name_out_len) { + int name_end = -1; + int j = *idx; + int ptr_count = 0; +#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0) +#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0) +#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0) + + char *cp = name_out; + const char *const end = name_out + name_out_len; + + /* Normally, names are a series of length prefixed strings terminated */ + /* with a length of 0 (the lengths are u8's < 63). */ + /* However, the length can start with a pair of 1 bits and that */ + /* means that the next 14 bits are a pointer within the current */ + /* packet. */ + + for(;;) { + u8 label_len; + if (j >= length) return -1; + GET8(label_len); + if (!label_len) break; + if (label_len & 0xc0) { + u8 ptr_low; + GET8(ptr_low); + if (name_end < 0) name_end = j; + j = (((int)label_len & 0x3f) << 8) + ptr_low; + /* Make sure that the target offset is in-bounds. */ + if (j < 0 || j >= length) return -1; + /* If we've jumped more times than there are characters in the + * message, we must have a loop. */ + if (++ptr_count > length) return -1; + continue; + } + if (label_len > 63) return -1; + if (cp != name_out) { + if (cp + 1 >= end) return -1; + *cp++ = '.'; + } + if (cp + label_len >= end) return -1; + memcpy(cp, packet + j, label_len); + cp += label_len; + j += label_len; + } + if (cp >= end) return -1; + *cp = '\0'; + if (name_end < 0) + *idx = j; + else + *idx = name_end; + return 0; + err: + return -1; +} + +/* parses a raw request from a nameserver */ +static int +reply_parse(u8 *packet, int length) { + int j = 0, k = 0; /* index into packet */ + u16 _t; /* used by the macros */ + u32 _t32; /* used by the macros */ + char tmp_name[256], cmp_name[256]; /* used by the macros */ + + u16 trans_id, questions, answers, authority, additional, datalength; + u16 flags = 0; + u32 ttl, ttl_r = 0xffffffff; + struct reply reply; + struct request *req = NULL; + unsigned int i; + + GET16(trans_id); + GET16(flags); + GET16(questions); + GET16(answers); + GET16(authority); + GET16(additional); + (void) authority; /* suppress "unused variable" warnings. */ + (void) additional; /* suppress "unused variable" warnings. */ + + req = request_find_from_trans_id(trans_id); + if (!req) return -1; + + memset(&reply, 0, sizeof(reply)); + + /* If it's not an answer, it doesn't correspond to any request. */ + if (!(flags & 0x8000)) return -1; /* must be an answer */ + if (flags & 0x020f) { + /* there was an error */ + goto err; + } + /* if (!answers) return; */ /* must have an answer of some form */ + + /* This macro skips a name in the DNS reply. */ +#define SKIP_NAME \ + do { tmp_name[0] = '\0'; \ + if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)\ + goto err; \ + } while(0) +#define TEST_NAME \ + do { tmp_name[0] = '\0'; \ + cmp_name[0] = '\0'; \ + k = j; \ + if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)\ + goto err; \ + if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \ + goto err; \ + if (memcmp(tmp_name, cmp_name, strlen (tmp_name)) != 0) \ + return (-1); /* we ignore mismatching names */ \ + } while(0) + + reply.type = req->request_type; + + /* skip over each question in the reply */ + for (i = 0; i < questions; ++i) { + /* the question looks like + * + */ + TEST_NAME; + j += 4; + if (j > length) goto err; + } + + /* now we have the answer section which looks like + * + */ + + for (i = 0; i < answers; ++i) { + u16 type, class; + + SKIP_NAME; + GET16(type); + GET16(class); + GET32(ttl); + GET16(datalength); + + if (type == TYPE_A && class == CLASS_INET) { + int addrcount, addrtocopy; + if (req->request_type != TYPE_A) { + j += datalength; continue; + } + if ((datalength & 3) != 0) /* not an even number of As. */ + goto err; + addrcount = datalength >> 2; + addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount); + + ttl_r = MIN(ttl_r, ttl); + /* we only bother with the first four addresses. */ + if (j + 4*addrtocopy > length) goto err; + memcpy(&reply.data.a.addresses[reply.data.a.addrcount], + packet + j, 4*addrtocopy); + j += 4*addrtocopy; + reply.data.a.addrcount += addrtocopy; + reply.have_answer = 1; + if (reply.data.a.addrcount == MAX_ADDRS) break; + } else if (type == TYPE_PTR && class == CLASS_INET) { + if (req->request_type != TYPE_PTR) { + j += datalength; continue; + } + if (name_parse(packet, length, &j, reply.data.ptr.name, + sizeof(reply.data.ptr.name))<0) + goto err; + ttl_r = MIN(ttl_r, ttl); + reply.have_answer = 1; + break; + } else if (type == TYPE_AAAA && class == CLASS_INET) { + int addrcount, addrtocopy; + if (req->request_type != TYPE_AAAA) { + j += datalength; continue; + } + if ((datalength & 15) != 0) /* not an even number of AAAAs. */ + goto err; + addrcount = datalength >> 4; /* each address is 16 bytes long */ + addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount); + ttl_r = MIN(ttl_r, ttl); + + /* we only bother with the first four addresses. */ + if (j + 16*addrtocopy > length) goto err; + memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount], + packet + j, 16*addrtocopy); + reply.data.aaaa.addrcount += addrtocopy; + j += 16*addrtocopy; + reply.have_answer = 1; + if (reply.data.aaaa.addrcount == MAX_ADDRS) break; + } else { + /* skip over any other type of resource */ + j += datalength; + } + } + + reply_handle(req, flags, ttl_r, &reply); + return 0; + err: + if (req) + reply_handle(req, flags, 0, NULL); + return -1; +} + +/* Parse a raw request (packet,length) sent to a nameserver port (port) from */ +/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */ +/* callback. */ +static int +request_parse(u8 *packet, int length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen) +{ + int j = 0; /* index into packet */ + u16 _t; /* used by the macros */ + char tmp_name[256]; /* used by the macros */ + + int i; + u16 trans_id, flags, questions, answers, authority, additional; + struct server_request *server_req = NULL; + + /* Get the header fields */ + GET16(trans_id); + GET16(flags); + GET16(questions); + GET16(answers); + GET16(authority); + GET16(additional); + + if (flags & 0x8000) return -1; /* Must not be an answer. */ + flags &= 0x0110; /* Only RD and CD get preserved. */ + + server_req = malloc(sizeof(struct server_request)); + if (server_req == NULL) return -1; + memset(server_req, 0, sizeof(struct server_request)); + + server_req->trans_id = trans_id; + memcpy(&server_req->addr, addr, addrlen); + server_req->addrlen = addrlen; + + server_req->base.flags = flags; + server_req->base.nquestions = 0; + server_req->base.questions = malloc(sizeof(struct evdns_server_question *) * questions); + if (server_req->base.questions == NULL) + goto err; + + for (i = 0; i < questions; ++i) { + u16 type, class; + struct evdns_server_question *q; + int namelen; + if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0) + goto err; + GET16(type); + GET16(class); + namelen = strlen(tmp_name); + q = malloc(sizeof(struct evdns_server_question) + namelen); + if (!q) + goto err; + q->type = type; + q->dns_question_class = class; + memcpy(q->name, tmp_name, namelen+1); + server_req->base.questions[server_req->base.nquestions++] = q; + } + + /* Ignore answers, authority, and additional. */ + + 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; +err: + if (server_req) { + if (server_req->base.questions) { + for (i = 0; i < server_req->base.nquestions; ++i) + free(server_req->base.questions[i]); + free(server_req->base.questions); + } + free(server_req); + } + return -1; + +#undef SKIP_NAME +#undef GET32 +#undef GET16 +#undef GET8 +} + +static u16 +default_transaction_id_fn(void) +{ + u16 trans_id; +#ifdef DNS_USE_CPU_CLOCK_FOR_ID + struct timespec ts; + static int clkid = -1; + if (clkid == -1) { + clkid = CLOCK_REALTIME; +#ifdef CLOCK_MONOTONIC + if (clock_gettime(CLOCK_MONOTONIC, &ts) != -1) + clkid = CLOCK_MONOTONIC; +#endif + } + if (clock_gettime(clkid, &ts) == -1) + 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; + evutil_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; + evutil_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; + u16 trans_id = trans_id_function(); + + if (trans_id == 0xffff) continue; + /* now check to see if that id is already inflight */ + req = started_at = req_head; + if (req) { + do { + if (req->trans_id == trans_id) break; + req = req->next; + } while (req != started_at); + } + /* we didn't find it, so this is a good id */ + if (req == started_at) return trans_id; + } +} + +/* choose a namesever to use. This function will try to ignore */ +/* nameservers which we think are down and load balance across the rest */ +/* by updating the server_head global each time. */ +static struct nameserver * +nameserver_pick(void) { + struct nameserver *started_at = server_head, *picked; + if (!server_head) return NULL; + + /* if we don't have any good nameservers then there's no */ + /* point in trying to find one. */ + if (!global_good_nameservers) { + server_head = server_head->next; + return server_head; + } + + /* remember that nameservers are in a circular list */ + for (;;) { + if (server_head->state) { + /* we think this server is currently good */ + picked = server_head; + server_head = server_head->next; + return picked; + } + + server_head = server_head->next; + if (server_head == started_at) { + /* all the nameservers seem to be down */ + /* so we just return this one and hope for the */ + /* best */ + assert(global_good_nameservers == 0); + picked = server_head; + server_head = server_head->next; + return picked; + } + } +} + +static int +address_is_correct(struct nameserver *ns, struct sockaddr *sa, socklen_t slen) +{ + struct sockaddr_in *sin = (struct sockaddr_in*) sa; + if (sa->sa_family != AF_INET || slen != sizeof(struct sockaddr_in)) + return 0; + if (sin->sin_addr.s_addr != ns->address) + return 0; + return 1; +} + +/* this is called when a namesever socket is ready for reading */ +static void +nameserver_read(struct nameserver *ns) { + u8 packet[1500]; + struct sockaddr_storage ss; + socklen_t addrlen = sizeof(ss); + + for (;;) { + const int r = recvfrom(ns->socket, packet, sizeof(packet), 0, + (struct sockaddr*)&ss, &addrlen); + if (r < 0) { + int err = last_error(ns->socket); + if (error_is_eagain(err)) return; + nameserver_failed(ns, strerror(err)); + return; + } + if (!address_is_correct(ns, (struct sockaddr*)&ss, addrlen)) { + log(EVDNS_LOG_WARN, "Address mismatch on received " + "DNS packet."); + return; + } + ns->timedout = 0; + reply_parse(packet, r); + } +} + +/* Read a packet from a DNS client on a server port s, parse it, and */ +/* act accordingly. */ +static void +server_port_read(struct evdns_server_port *s) { + u8 packet[1500]; + struct sockaddr_storage addr; + socklen_t addrlen; + int r; + + for (;;) { + addrlen = sizeof(struct sockaddr_storage); + r = recvfrom(s->socket, packet, sizeof(packet), 0, + (struct sockaddr*) &addr, &addrlen); + if (r < 0) { + int err = last_error(s->socket); + if (error_is_eagain(err)) return; + log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.", + strerror(err), err); + return; + } + request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen); + } +} + +/* Try to write all pending replies on a given DNS server port. */ +static void +server_port_flush(struct evdns_server_port *port) +{ + while (port->pending_replies) { + struct server_request *req = port->pending_replies; + int 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)) + return; + log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", strerror(err), err); + } + if (server_request_free(req)) { + /* we released the last reference to req->port. */ + return; + } + } + + /* We have no more pending requests; stop listening for 'writeable' events. */ + (void) event_del(&port->event); + event_set(&port->event, port->socket, EV_READ | EV_PERSIST, + server_port_ready_callback, port); + if (event_add(&port->event, NULL) < 0) { + log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server."); + /* ???? Do more? */ + } +} + +/* set if we are waiting for the ability to write to this server. */ +/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */ +/* we stop these events. */ +static void +nameserver_write_waiting(struct nameserver *ns, char waiting) { + if (ns->write_waiting == waiting) return; + + ns->write_waiting = waiting; + (void) event_del(&ns->event); + event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST, + nameserver_ready_callback, ns); + if (event_add(&ns->event, NULL) < 0) { + log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s", + debug_ntoa(ns->address)); + /* ???? Do more? */ + } +} + +/* a callback function. Called by libevent when the kernel says that */ +/* a nameserver socket is ready for writing or reading */ +static void +nameserver_ready_callback(int fd, short events, void *arg) { + struct nameserver *ns = (struct nameserver *) arg; + (void)fd; + + if (events & EV_WRITE) { + ns->choked = 0; + if (!evdns_transmit()) { + nameserver_write_waiting(ns, 0); + } + } + if (events & EV_READ) { + nameserver_read(ns); + } +} + +/* a callback function. Called by libevent when the kernel says that */ +/* a server socket is ready for writing or reading. */ +static void +server_port_ready_callback(int fd, short events, void *arg) { + struct evdns_server_port *port = (struct evdns_server_port *) arg; + (void) fd; + + if (events & EV_WRITE) { + port->choked = 0; + server_port_flush(port); + } + if (events & EV_READ) { + server_port_read(port); + } +} + +/* This is an inefficient representation; only use it via the dnslabel_table_* + * functions, so that is can be safely replaced with something smarter later. */ +#define MAX_LABELS 128 +/* Structures used to implement name compression */ +struct dnslabel_entry { char *v; off_t pos; }; +struct dnslabel_table { + int n_labels; /* number of current entries */ + /* map from name to position in message */ + struct dnslabel_entry labels[MAX_LABELS]; +}; + +/* Initialize dnslabel_table. */ +static void +dnslabel_table_init(struct dnslabel_table *table) +{ + table->n_labels = 0; +} + +/* Free all storage held by table, but not the table itself. */ +static void +dnslabel_clear(struct dnslabel_table *table) +{ + int i; + for (i = 0; i < table->n_labels; ++i) + free(table->labels[i].v); + table->n_labels = 0; +} + +/* return the position of the label in the current message, or -1 if the label */ +/* hasn't been used yet. */ +static int +dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label) +{ + int i; + for (i = 0; i < table->n_labels; ++i) { + if (!strcmp(label, table->labels[i].v)) + return table->labels[i].pos; + } + return -1; +} + +/* remember that we've used the label at position pos */ +static int +dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos) +{ + char *v; + int p; + if (table->n_labels == MAX_LABELS) + return (-1); + v = strdup(label); + if (v == NULL) + return (-1); + p = table->n_labels++; + table->labels[p].v = v; + table->labels[p].pos = pos; + + return (0); +} + +/* Converts a string to a length-prefixed set of DNS labels, starting */ +/* at buf[j]. name and buf must not overlap. name_len should be the length */ +/* of name. table is optional, and is used for compression. */ +/* */ +/* Input: abc.def */ +/* Output: <3>abc<3>def<0> */ +/* */ +/* Returns the first index after the encoded name, or negative on error. */ +/* -1 label was > 63 bytes */ +/* -2 name too long to fit in buffer. */ +/* */ +static off_t +dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j, + const char *name, const int name_len, + struct dnslabel_table *table) { + const char *end = name + name_len; + int ref = 0; + u16 _t; + +#define APPEND16(x) do { \ + if (j + 2 > (off_t)buf_len) \ + goto overflow; \ + _t = htons(x); \ + memcpy(buf + j, &_t, 2); \ + j += 2; \ + } while (0) +#define APPEND32(x) do { \ + if (j + 4 > (off_t)buf_len) \ + goto overflow; \ + _t32 = htonl(x); \ + memcpy(buf + j, &_t32, 4); \ + j += 4; \ + } while (0) + + if (name_len > 255) return -2; + + for (;;) { + const char *const start = name; + if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) { + APPEND16(ref | 0xc000); + return j; + } + name = strchr(name, '.'); + if (!name) { + const unsigned int label_len = end - start; + if (label_len > 63) return -1; + if ((size_t)(j+label_len+1) > buf_len) return -2; + if (table) dnslabel_table_add(table, start, j); + buf[j++] = label_len; + + memcpy(buf + j, start, end - start); + j += end - start; + break; + } else { + /* append length of the label. */ + const unsigned int label_len = name - start; + if (label_len > 63) return -1; + if ((size_t)(j+label_len+1) > buf_len) return -2; + if (table) dnslabel_table_add(table, start, j); + buf[j++] = label_len; + + memcpy(buf + j, start, name - start); + j += name - start; + /* hop over the '.' */ + name++; + } + } + + /* the labels must be terminated by a 0. */ + /* It's possible that the name ended in a . */ + /* in which case the zero is already there */ + if (!j || buf[j-1]) buf[j++] = 0; + return j; + overflow: + return (-2); +} + +/* Finds the length of a dns request for a DNS name of the given */ +/* length. The actual request may be smaller than the value returned */ +/* here */ +static int +evdns_request_len(const int name_len) { + return 96 + /* length of the DNS standard header */ + name_len + 2 + + 4; /* space for the resource type */ +} + +/* build a dns request packet into buf. buf should be at least as long */ +/* as evdns_request_len told you it should be. */ +/* */ +/* Returns the amount of space used. Negative on error. */ +static int +evdns_request_data_build(const char *const name, const int name_len, + const u16 trans_id, const u16 type, const u16 class, + u8 *const buf, size_t buf_len) { + off_t j = 0; /* current offset into buf */ + u16 _t; /* used by the macros */ + + APPEND16(trans_id); + APPEND16(0x0100); /* standard query, recusion needed */ + APPEND16(1); /* one question */ + APPEND16(0); /* no answers */ + APPEND16(0); /* no authority */ + APPEND16(0); /* no additional */ + + j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL); + if (j < 0) { + return (int)j; + } + + APPEND16(type); + APPEND16(class); + + return (int)j; + overflow: + return (-1); +} + +/* exported function */ +struct evdns_server_port * +evdns_add_server_port(int socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data) +{ + struct evdns_server_port *port; + if (!(port = malloc(sizeof(struct evdns_server_port)))) + return NULL; + memset(port, 0, sizeof(struct evdns_server_port)); + + assert(!is_tcp); /* TCP sockets not yet implemented */ + port->socket = socket; + port->refcnt = 1; + port->choked = 0; + port->closing = 0; + port->user_callback = cb; + port->user_data = user_data; + port->pending_replies = NULL; + + event_set(&port->event, port->socket, EV_READ | EV_PERSIST, + server_port_ready_callback, port); + event_add(&port->event, NULL); /* check return. */ + return port; +} + +/* exported function */ +void +evdns_close_server_port(struct evdns_server_port *port) +{ + if (--port->refcnt == 0) + server_port_free(port); + port->closing = 1; +} + +/* exported function */ +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) +{ + struct server_request *req = TO_SERVER_REQUEST(_req); + struct server_reply_item **itemp, *item; + int *countp; + + if (req->response) /* have we already answered? */ + return (-1); + + switch (section) { + case EVDNS_ANSWER_SECTION: + itemp = &req->answer; + countp = &req->n_answer; + break; + case EVDNS_AUTHORITY_SECTION: + itemp = &req->authority; + countp = &req->n_authority; + break; + case EVDNS_ADDITIONAL_SECTION: + itemp = &req->additional; + countp = &req->n_additional; + break; + default: + return (-1); + } + while (*itemp) { + itemp = &((*itemp)->next); + } + item = malloc(sizeof(struct server_reply_item)); + if (!item) + return -1; + item->next = NULL; + if (!(item->name = strdup(name))) { + free(item); + return -1; + } + item->type = type; + item->dns_question_class = class; + item->ttl = ttl; + item->is_name = is_name != 0; + item->datalen = 0; + item->data = NULL; + if (data) { + if (item->is_name) { + if (!(item->data = strdup(data))) { + free(item->name); + free(item); + return -1; + } + item->datalen = (u16)-1; + } else { + if (!(item->data = malloc(datalen))) { + free(item->name); + free(item); + return -1; + } + item->datalen = datalen; + memcpy(item->data, data, datalen); + } + } + + *itemp = item; + ++(*countp); + return 0; +} + +/* exported function */ +int +evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) +{ + return evdns_server_request_add_reply( + req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, + ttl, n*4, 0, addrs); +} + +/* exported function */ +int +evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) +{ + return evdns_server_request_add_reply( + req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET, + ttl, n*16, 0, addrs); +} + +/* exported function */ +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) +{ + u32 a; + char buf[32]; + assert(in || inaddr_name); + assert(!(in && inaddr_name)); + if (in) { + a = ntohl(in->s_addr); + evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", + (int)(u8)((a )&0xff), + (int)(u8)((a>>8 )&0xff), + (int)(u8)((a>>16)&0xff), + (int)(u8)((a>>24)&0xff)); + inaddr_name = buf; + } + return evdns_server_request_add_reply( + req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET, + ttl, -1, 1, hostname); +} + +/* exported function */ +int +evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl) +{ + return evdns_server_request_add_reply( + req, EVDNS_ANSWER_SECTION, name, TYPE_CNAME, CLASS_INET, + ttl, -1, 1, cname); +} + + +static int +evdns_server_request_format_response(struct server_request *req, int err) +{ + unsigned char buf[1500]; + size_t buf_len = sizeof(buf); + off_t j = 0, r; + u16 _t; + u32 _t32; + int i; + u16 flags; + struct dnslabel_table table; + + if (err < 0 || err > 15) return -1; + + /* Set response bit and error code; copy OPCODE and RD fields from + * question; copy RA and AA if set by caller. */ + flags = req->base.flags; + flags |= (0x8000 | err); + + dnslabel_table_init(&table); + APPEND16(req->trans_id); + APPEND16(flags); + APPEND16(req->base.nquestions); + APPEND16(req->n_answer); + APPEND16(req->n_authority); + APPEND16(req->n_additional); + + /* Add questions. */ + for (i=0; i < req->base.nquestions; ++i) { + const char *s = req->base.questions[i]->name; + j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table); + if (j < 0) { + dnslabel_clear(&table); + return (int) j; + } + APPEND16(req->base.questions[i]->type); + APPEND16(req->base.questions[i]->dns_question_class); + } + + /* Add answer, authority, and additional sections. */ + for (i=0; i<3; ++i) { + struct server_reply_item *item; + if (i==0) + item = req->answer; + else if (i==1) + item = req->authority; + else + item = req->additional; + while (item) { + r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table); + if (r < 0) + goto overflow; + j = r; + + APPEND16(item->type); + APPEND16(item->dns_question_class); + APPEND32(item->ttl); + if (item->is_name) { + off_t len_idx = j, name_start; + j += 2; + name_start = j; + r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table); + if (r < 0) + goto overflow; + j = r; + _t = htons( (short) (j-name_start) ); + memcpy(buf+len_idx, &_t, 2); + } else { + APPEND16(item->datalen); + if (j+item->datalen > (off_t)buf_len) + goto overflow; + memcpy(buf+j, item->data, item->datalen); + j += item->datalen; + } + item = item->next; + } + } + + if (j > 512) { +overflow: + j = 512; + buf[2] |= 0x02; /* set the truncated bit. */ + } + + req->response_len = j; + + if (!(req->response = malloc(req->response_len))) { + server_request_free_answers(req); + dnslabel_clear(&table); + return (-1); + } + memcpy(req->response, buf, req->response_len); + server_request_free_answers(req); + dnslabel_clear(&table); + return (0); +} + +/* exported function */ +int +evdns_server_request_respond(struct evdns_server_request *_req, int err) +{ + struct server_request *req = TO_SERVER_REQUEST(_req); + struct evdns_server_port *port = req->port; + int r; + if (!req->response) { + if ((r = evdns_server_request_format_response(req, err))<0) + return r; + } + + r = sendto(port->socket, req->response, req->response_len, 0, + (struct sockaddr*) &req->addr, req->addrlen); + if (r<0) { + int sock_err = last_error(port->socket); + if (! error_is_eagain(sock_err)) + return -1; + + if (port->pending_replies) { + req->prev_pending = port->pending_replies->prev_pending; + req->next_pending = port->pending_replies; + req->prev_pending->next_pending = + req->next_pending->prev_pending = req; + } else { + req->prev_pending = req->next_pending = req; + port->pending_replies = req; + port->choked = 1; + + (void) event_del(&port->event); + event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port); + + if (event_add(&port->event, NULL) < 0) { + log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server"); + } + + } + + return 1; + } + if (server_request_free(req)) + return 0; + + if (port->pending_replies) + server_port_flush(port); + + return 0; +} + +/* Free all storage held by RRs in req. */ +static void +server_request_free_answers(struct server_request *req) +{ + struct server_reply_item *victim, *next, **list; + int i; + for (i = 0; i < 3; ++i) { + if (i==0) + list = &req->answer; + else if (i==1) + list = &req->authority; + else + list = &req->additional; + + victim = *list; + while (victim) { + next = victim->next; + free(victim->name); + if (victim->data) + free(victim->data); + free(victim); + victim = next; + } + *list = NULL; + } +} + +/* Free all storage held by req, and remove links to it. */ +/* return true iff we just wound up freeing the server_port. */ +static int +server_request_free(struct server_request *req) +{ + int i, rc=1; + if (req->base.questions) { + for (i = 0; i < req->base.nquestions; ++i) + free(req->base.questions[i]); + free(req->base.questions); + } + + if (req->port) { + if (req->port->pending_replies == req) { + if (req->next_pending) + req->port->pending_replies = req->next_pending; + else + req->port->pending_replies = NULL; + } + rc = --req->port->refcnt; + } + + if (req->response) { + free(req->response); + } + + server_request_free_answers(req); + + if (req->next_pending && req->next_pending != req) { + req->next_pending->prev_pending = req->prev_pending; + req->prev_pending->next_pending = req->next_pending; + } + + if (rc == 0) { + server_port_free(req->port); + free(req); + return (1); + } + free(req); + return (0); +} + +/* Free all storage held by an evdns_server_port. Only called when */ +static void +server_port_free(struct evdns_server_port *port) +{ + assert(port); + assert(!port->refcnt); + assert(!port->pending_replies); + if (port->socket > 0) { + CLOSE_SOCKET(port->socket); + port->socket = -1; + } + (void) event_del(&port->event); + /* XXXX actually free the port? -NM */ +} + +/* exported function */ +int +evdns_server_request_drop(struct evdns_server_request *_req) +{ + struct server_request *req = TO_SERVER_REQUEST(_req); + server_request_free(req); + return 0; +} + +/* exported function */ +int +evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len) +{ + struct server_request *req = TO_SERVER_REQUEST(_req); + if (addr_len < (int)req->addrlen) + return -1; + memcpy(sa, &(req->addr), req->addrlen); + return req->addrlen; +} + +#undef APPEND16 +#undef APPEND32 + +/* this is a libevent callback function which is called when a request */ +/* has timed out. */ +static void +evdns_request_timeout_callback(int fd, short events, void *arg) { + struct request *const req = (struct request *) arg; + (void) fd; + (void) events; + + log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg); + + req->ns->timedout++; + if (req->ns->timedout > global_max_nameserver_timeout) { + req->ns->timedout = 0; + nameserver_failed(req->ns, "request timed out."); + } + + (void) evtimer_del(&req->timeout_event); + if (req->tx_count >= global_max_retransmits) { + /* this request has failed */ + reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); + request_finished(req, &req_head); + } else { + /* retransmit it */ + evdns_request_transmit(req); + } +} + +/* try to send a request to a given server. */ +/* */ +/* return: */ +/* 0 ok */ +/* 1 temporary failure */ +/* 2 other failure */ +static int +evdns_request_transmit_to(struct request *req, struct nameserver *server) { + struct sockaddr_in sin; + int r; + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = req->ns->address; + sin.sin_port = req->ns->port; + sin.sin_family = AF_INET; + + r = sendto(server->socket, req->request, req->request_len, 0, + (struct sockaddr*)&sin, sizeof(sin)); + if (r < 0) { + int err = last_error(server->socket); + if (error_is_eagain(err)) return 1; + nameserver_failed(req->ns, strerror(err)); + return 2; + } else if (r != (int)req->request_len) { + return 1; /* short write */ + } else { + return 0; + } +} + +/* try to send a request, updating the fields of the request */ +/* as needed */ +/* */ +/* return: */ +/* 0 ok */ +/* 1 failed */ +static int +evdns_request_transmit(struct request *req) { + int retcode = 0, r; + + /* if we fail to send this packet then this flag marks it */ + /* for evdns_transmit */ + req->transmit_me = 1; + if (req->trans_id == 0xffff) abort(); + + if (req->ns->choked) { + /* don't bother trying to write to a socket */ + /* which we have had EAGAIN from */ + return 1; + } + + r = evdns_request_transmit_to(req, req->ns); + switch (r) { + case 1: + /* temp failure */ + req->ns->choked = 1; + nameserver_write_waiting(req->ns, 1); + return 1; + case 2: + /* failed in some other way */ + retcode = 1; + /* fall through */ + default: + /* all ok */ + log(EVDNS_LOG_DEBUG, + "Setting timeout for request %lx", (unsigned long) req); + if (evtimer_add(&req->timeout_event, &global_timeout) < 0) { + log(EVDNS_LOG_WARN, + "Error from libevent when adding timer for request %lx", + (unsigned long) req); + /* ???? Do more? */ + } + req->tx_count++; + req->transmit_me = 0; + return retcode; + } +} + +static void +nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) { + struct nameserver *const ns = (struct nameserver *) arg; + (void) type; + (void) count; + (void) ttl; + (void) addresses; + + if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) { + /* this is a good reply */ + nameserver_up(ns); + } else nameserver_probe_failed(ns); +} + +static void +nameserver_send_probe(struct nameserver *const ns) { + struct request *req; + /* here we need to send a probe to a given nameserver */ + /* in the hope that it is up now. */ + + log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntoa(ns->address)); + + req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, ns); + if (!req) return; + /* we force this into the inflight queue no matter what */ + request_trans_id_set(req, transaction_id_pick()); + req->ns = ns; + request_submit(req); +} + +/* returns: */ +/* 0 didn't try to transmit anything */ +/* 1 tried to transmit something */ +static int +evdns_transmit(void) { + char did_try_to_transmit = 0; + + if (req_head) { + struct request *const started_at = req_head, *req = req_head; + /* first transmit all the requests which are currently waiting */ + do { + if (req->transmit_me) { + did_try_to_transmit = 1; + evdns_request_transmit(req); + } + + req = req->next; + } while (req != started_at); + } + + return did_try_to_transmit; +} + +/* exported function */ +int +evdns_count_nameservers(void) +{ + const struct nameserver *server = server_head; + int n = 0; + if (!server) + return 0; + do { + ++n; + server = server->next; + } while (server != server_head); + return n; +} + +/* exported function */ +int +evdns_clear_nameservers_and_suspend(void) +{ + struct nameserver *server = server_head, *started_at = server_head; + struct request *req = req_head, *req_started_at = req_head; + + if (!server) + return 0; + while (1) { + struct nameserver *next = server->next; + (void) event_del(&server->event); + if (evtimer_initialized(&server->timeout_event)) + (void) evtimer_del(&server->timeout_event); + if (server->socket >= 0) + CLOSE_SOCKET(server->socket); + free(server); + if (next == started_at) + break; + server = next; + } + server_head = NULL; + global_good_nameservers = 0; + + while (req) { + struct request *next = req->next; + req->tx_count = req->reissue_count = 0; + req->ns = NULL; + /* ???? What to do about searches? */ + (void) evtimer_del(&req->timeout_event); + req->trans_id = 0; + req->transmit_me = 0; + + global_requests_waiting++; + evdns_request_insert(req, &req_waiting_head); + /* We want to insert these suspended elements at the front of + * the waiting queue, since they were pending before any of + * the waiting entries were added. This is a circular list, + * so we can just shift the start back by one.*/ + req_waiting_head = req_waiting_head->prev; + + if (next == req_started_at) + break; + req = next; + } + req_head = NULL; + global_requests_inflight = 0; + + return 0; +} + + +/* exported function */ +int +evdns_resume(void) +{ + evdns_requests_pump_waiting_queue(); + return 0; +} + +static int +_evdns_nameserver_add_impl(unsigned long int address, int port) { + /* first check to see if we already have this nameserver */ + + const struct nameserver *server = server_head, *const started_at = server_head; + struct nameserver *ns; + int err = 0; + if (server) { + do { + if (server->address == address) return 3; + server = server->next; + } while (server != started_at); + } + + ns = (struct nameserver *) malloc(sizeof(struct nameserver)); + if (!ns) return -1; + + memset(ns, 0, sizeof(struct nameserver)); + + evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns); + + ns->socket = socket(PF_INET, SOCK_DGRAM, 0); + if (ns->socket < 0) { err = 1; goto out1; } + evutil_make_socket_nonblocking(ns->socket); + + ns->address = address; + ns->port = htons(port); + ns->state = 1; + event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns); + if (event_add(&ns->event, NULL) < 0) { + err = 2; + goto out2; + } + + log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntoa(address)); + + /* insert this nameserver into the list of them */ + if (!server_head) { + ns->next = ns->prev = ns; + server_head = ns; + } else { + ns->next = server_head->next; + ns->prev = server_head; + server_head->next = ns; + if (server_head->prev == server_head) { + server_head->prev = ns; + } + } + + global_good_nameservers++; + + return 0; + +out2: + CLOSE_SOCKET(ns->socket); +out1: + free(ns); + log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntoa(address), err); + return err; +} + +/* exported function */ +int +evdns_nameserver_add(unsigned long int address) { + return _evdns_nameserver_add_impl(address, 53); +} + +/* exported function */ +int +evdns_nameserver_ip_add(const char *ip_as_string) { + struct in_addr ina; + int port; + char buf[20]; + const char *cp; + cp = strchr(ip_as_string, ':'); + if (! cp) { + cp = ip_as_string; + port = 53; + } else { + port = strtoint(cp+1); + if (port < 0 || port > 65535) { + return 4; + } + if ((cp-ip_as_string) >= (int)sizeof(buf)) { + return 4; + } + memcpy(buf, ip_as_string, cp-ip_as_string); + buf[cp-ip_as_string] = '\0'; + cp = buf; + } + if (!inet_aton(cp, &ina)) { + return 4; + } + return _evdns_nameserver_add_impl(ina.s_addr, port); +} + +/* insert into the tail of the queue */ +static void +evdns_request_insert(struct request *req, struct request **head) { + if (!*head) { + *head = req; + req->next = req->prev = req; + return; + } + + req->prev = (*head)->prev; + req->prev->next = req; + req->next = *head; + (*head)->prev = req; +} + +static int +string_num_dots(const char *s) { + int count = 0; + while ((s = strchr(s, '.'))) { + s++; + count++; + } + return count; +} + +static struct request * +request_new(int type, const char *name, int flags, + evdns_callback_type callback, void *user_ptr) { + const char issuing_now = + (global_requests_inflight < global_max_requests_inflight) ? 1 : 0; + + const int name_len = strlen(name); + const int request_max_len = evdns_request_len(name_len); + const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff; + /* the request data is alloced in a single block with the header */ + struct request *const req = + (struct request *) malloc(sizeof(struct request) + request_max_len); + int rlen; + (void) flags; + + if (!req) return NULL; + memset(req, 0, sizeof(struct request)); + + evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req); + + /* request data lives just after the header */ + req->request = ((u8 *) req) + sizeof(struct request); + /* denotes that the request data shouldn't be free()ed */ + req->request_appended = 1; + rlen = evdns_request_data_build(name, name_len, trans_id, + type, CLASS_INET, req->request, request_max_len); + if (rlen < 0) + goto err1; + req->request_len = rlen; + req->trans_id = trans_id; + req->tx_count = 0; + req->request_type = type; + req->user_pointer = user_ptr; + req->user_callback = callback; + req->ns = issuing_now ? nameserver_pick() : NULL; + req->next = req->prev = NULL; + + return req; +err1: + free(req); + return NULL; +} + +static void +request_submit(struct request *const req) { + if (req->ns) { + /* if it has a nameserver assigned then this is going */ + /* straight into the inflight queue */ + evdns_request_insert(req, &req_head); + global_requests_inflight++; + evdns_request_transmit(req); + } else { + evdns_request_insert(req, &req_waiting_head); + global_requests_waiting++; + } +} + +/* exported function */ +int evdns_resolve_ipv4(const char *name, int flags, + evdns_callback_type callback, void *ptr) { + log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); + if (flags & DNS_QUERY_NO_SEARCH) { + struct request *const req = + request_new(TYPE_A, name, flags, callback, ptr); + if (req == NULL) + return (1); + request_submit(req); + return (0); + } else { + return (search_request_new(TYPE_A, name, flags, callback, ptr)); + } +} + +/* exported function */ +int evdns_resolve_ipv6(const char *name, int flags, + evdns_callback_type callback, void *ptr) { + log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name); + if (flags & DNS_QUERY_NO_SEARCH) { + struct request *const req = + request_new(TYPE_AAAA, name, flags, callback, ptr); + if (req == NULL) + return (1); + request_submit(req); + return (0); + } else { + return (search_request_new(TYPE_AAAA, name, flags, callback, ptr)); + } +} + +int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) { + char buf[32]; + struct request *req; + u32 a; + assert(in); + a = ntohl(in->s_addr); + evutil_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", + (int)(u8)((a )&0xff), + (int)(u8)((a>>8 )&0xff), + (int)(u8)((a>>16)&0xff), + (int)(u8)((a>>24)&0xff)); + log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf); + req = request_new(TYPE_PTR, buf, flags, callback, ptr); + if (!req) return 1; + request_submit(req); + return 0; +} + +int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) { + /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */ + char buf[73]; + char *cp; + struct request *req; + int i; + assert(in); + cp = buf; + for (i=15; i >= 0; --i) { + u8 byte = in->s6_addr[i]; + *cp++ = "0123456789abcdef"[byte & 0x0f]; + *cp++ = '.'; + *cp++ = "0123456789abcdef"[byte >> 4]; + *cp++ = '.'; + } + 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; + request_submit(req); + return 0; +} + +/*/////////////////////////////////////////////////////////////////// */ +/* Search support */ +/* */ +/* the libc resolver has support for searching a number of domains */ +/* to find a name. If nothing else then it takes the single domain */ +/* from the gethostname() call. */ +/* */ +/* It can also be configured via the domain and search options in a */ +/* resolv.conf. */ +/* */ +/* The ndots option controls how many dots it takes for the resolver */ +/* to decide that a name is non-local and so try a raw lookup first. */ + +struct search_domain { + int len; + struct search_domain *next; + /* the text string is appended to this structure */ +}; + +struct search_state { + int refcount; + int ndots; + int num_domains; + struct search_domain *head; +}; + +static struct search_state *global_search_state = NULL; + +static void +search_state_decref(struct search_state *const state) { + if (!state) return; + state->refcount--; + if (!state->refcount) { + struct search_domain *next, *dom; + for (dom = state->head; dom; dom = next) { + next = dom->next; + free(dom); + } + free(state); + } +} + +static struct search_state * +search_state_new(void) { + struct search_state *state = (struct search_state *) malloc(sizeof(struct search_state)); + if (!state) return NULL; + memset(state, 0, sizeof(struct search_state)); + state->refcount = 1; + state->ndots = 1; + + return state; +} + +static void +search_postfix_clear(void) { + search_state_decref(global_search_state); + + global_search_state = search_state_new(); +} + +/* exported function */ +void +evdns_search_clear(void) { + search_postfix_clear(); +} + +static void +search_postfix_add(const char *domain) { + int domain_len; + struct search_domain *sdomain; + while (domain[0] == '.') domain++; + domain_len = strlen(domain); + + if (!global_search_state) global_search_state = search_state_new(); + if (!global_search_state) return; + global_search_state->num_domains++; + + sdomain = (struct search_domain *) malloc(sizeof(struct search_domain) + domain_len); + if (!sdomain) return; + memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len); + sdomain->next = global_search_state->head; + sdomain->len = domain_len; + + global_search_state->head = sdomain; +} + +/* reverse the order of members in the postfix list. This is needed because, */ +/* when parsing resolv.conf we push elements in the wrong order */ +static void +search_reverse(void) { + struct search_domain *cur, *prev = NULL, *next; + cur = global_search_state->head; + while (cur) { + next = cur->next; + cur->next = prev; + prev = cur; + cur = next; + } + + global_search_state->head = prev; +} + +/* exported function */ +void +evdns_search_add(const char *domain) { + search_postfix_add(domain); +} + +/* exported function */ +void +evdns_search_ndots_set(const int ndots) { + if (!global_search_state) global_search_state = search_state_new(); + if (!global_search_state) return; + global_search_state->ndots = ndots; +} + +static void +search_set_from_hostname(void) { + char hostname[HOST_NAME_MAX + 1], *domainname; + + search_postfix_clear(); + if (gethostname(hostname, sizeof(hostname))) return; + domainname = strchr(hostname, '.'); + if (!domainname) return; + search_postfix_add(domainname); +} + +/* warning: returns malloced string */ +static char * +search_make_new(const struct search_state *const state, int n, const char *const base_name) { + const int base_len = strlen(base_name); + const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1; + struct search_domain *dom; + + for (dom = state->head; dom; dom = dom->next) { + if (!n--) { + /* this is the postfix we want */ + /* the actual postfix string is kept at the end of the structure */ + const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain); + const int postfix_len = dom->len; + char *const newname = (char *) malloc(base_len + need_to_append_dot + postfix_len + 1); + if (!newname) return NULL; + memcpy(newname, base_name, base_len); + if (need_to_append_dot) newname[base_len] = '.'; + memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len); + newname[base_len + need_to_append_dot + postfix_len] = 0; + return newname; + } + } + + /* we ran off the end of the list and still didn't find the requested string */ + abort(); + return NULL; /* unreachable; stops warnings in some compilers. */ +} + +static int +search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) { + assert(type == TYPE_A || type == TYPE_AAAA); + if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) && + global_search_state && + global_search_state->num_domains) { + /* we have some domains to search */ + struct request *req; + if (string_num_dots(name) >= global_search_state->ndots) { + req = request_new(type, name, flags, user_callback, user_arg); + if (!req) return 1; + req->search_index = -1; + } else { + char *const new_name = search_make_new(global_search_state, 0, name); + if (!new_name) return 1; + req = request_new(type, new_name, flags, user_callback, user_arg); + free(new_name); + if (!req) return 1; + req->search_index = 0; + } + req->search_origname = strdup(name); + req->search_state = global_search_state; + req->search_flags = flags; + global_search_state->refcount++; + request_submit(req); + return 0; + } else { + struct request *const req = request_new(type, name, flags, user_callback, user_arg); + if (!req) return 1; + request_submit(req); + return 0; + } +} + +/* this is called when a request has failed to find a name. We need to check */ +/* if it is part of a search and, if so, try the next name in the list */ +/* returns: */ +/* 0 another request has been submitted */ +/* 1 no more requests needed */ +static int +search_try_next(struct request *const req) { + if (req->search_state) { + /* it is part of a search */ + char *new_name; + struct request *newreq; + req->search_index++; + if (req->search_index >= req->search_state->num_domains) { + /* no more postfixes to try, however we may need to try */ + /* this name without a postfix */ + if (string_num_dots(req->search_origname) < req->search_state->ndots) { + /* yep, we need to try it raw */ + 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); + return 0; + } + } + return 1; + } + + new_name = search_make_new(req->search_state, req->search_index, req->search_origname); + if (!new_name) return 1; + log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index); + newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer); + free(new_name); + if (!newreq) return 1; + newreq->search_origname = req->search_origname; + req->search_origname = NULL; + newreq->search_state = req->search_state; + newreq->search_flags = req->search_flags; + newreq->search_index = req->search_index; + newreq->search_state->refcount++; + request_submit(newreq); + return 0; + } + return 1; +} + +static void +search_request_finished(struct request *const req) { + if (req->search_state) { + search_state_decref(req->search_state); + req->search_state = NULL; + } + if (req->search_origname) { + free(req->search_origname); + req->search_origname = NULL; + } +} + +/*/////////////////////////////////////////////////////////////////// */ +/* Parsing resolv.conf files */ + +static void +evdns_resolv_set_defaults(int flags) { + /* if the file isn't found then we assume a local resolver */ + if (flags & DNS_OPTION_SEARCH) search_set_from_hostname(); + if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1"); +} + +#ifndef HAVE_STRTOK_R +static char * +strtok_r(char *s, const char *delim, char **state) { + return strtok(s, delim); +} +#endif + +/* helper version of atoi which returns -1 on error */ +static int +strtoint(const char *const str) { + char *endptr; + const int r = strtol(str, &endptr, 10); + if (*endptr) return -1; + return r; +} + +/* helper version of atoi that returns -1 on error and clips to bounds. */ +static int +strtoint_clipped(const char *const str, int min, int max) +{ + int r = strtoint(str); + if (r == -1) + return r; + else if (rmax) + return max; + else + return r; +} + +/* exported function */ +int +evdns_set_option(const char *option, const char *val, int flags) +{ + if (!strncmp(option, "ndots:", 6)) { + const int ndots = strtoint(val); + if (ndots == -1) return -1; + if (!(flags & DNS_OPTION_SEARCH)) return 0; + log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots); + if (!global_search_state) global_search_state = search_state_new(); + if (!global_search_state) return -1; + global_search_state->ndots = ndots; + } else if (!strncmp(option, "timeout:", 8)) { + const int timeout = strtoint(val); + if (timeout == -1) return -1; + if (!(flags & DNS_OPTION_MISC)) return 0; + log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout); + global_timeout.tv_sec = timeout; + } else if (!strncmp(option, "max-timeouts:", 12)) { + const int maxtimeout = strtoint_clipped(val, 1, 255); + if (maxtimeout == -1) return -1; + if (!(flags & DNS_OPTION_MISC)) return 0; + log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d", + maxtimeout); + global_max_nameserver_timeout = maxtimeout; + } else if (!strncmp(option, "max-inflight:", 13)) { + const int maxinflight = strtoint_clipped(val, 1, 65000); + if (maxinflight == -1) return -1; + if (!(flags & DNS_OPTION_MISC)) return 0; + log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", + maxinflight); + global_max_requests_inflight = maxinflight; + } else if (!strncmp(option, "attempts:", 9)) { + int retries = strtoint(val); + if (retries == -1) return -1; + if (retries > 255) retries = 255; + if (!(flags & DNS_OPTION_MISC)) return 0; + log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries); + global_max_retransmits = retries; + } + return 0; +} + +static void +resolv_conf_parse_line(char *const start, int flags) { + char *strtok_state; + static const char *const delims = " \t"; +#define NEXT_TOKEN strtok_r(NULL, delims, &strtok_state) + + char *const first_token = strtok_r(start, delims, &strtok_state); + if (!first_token) return; + + if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) { + const char *const nameserver = NEXT_TOKEN; + struct in_addr ina; + + if (inet_aton(nameserver, &ina)) { + /* address is valid */ + evdns_nameserver_add(ina.s_addr); + } + } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) { + const char *const domain = NEXT_TOKEN; + if (domain) { + search_postfix_clear(); + search_postfix_add(domain); + } + } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) { + const char *domain; + search_postfix_clear(); + + while ((domain = NEXT_TOKEN)) { + search_postfix_add(domain); + } + search_reverse(); + } else if (!strcmp(first_token, "options")) { + const char *option; + while ((option = NEXT_TOKEN)) { + const char *val = strchr(option, ':'); + evdns_set_option(option, val ? val+1 : "", flags); + } + } +#undef NEXT_TOKEN +} + +/* exported function */ +/* returns: */ +/* 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 */ +int +evdns_resolv_conf_parse(int flags, const char *const filename) { + struct stat st; + int fd, n, r; + u8 *resolv; + char *start; + int err = 0; + + log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + evdns_resolv_set_defaults(flags); + return 1; + } + + if (fstat(fd, &st)) { err = 2; goto out1; } + if (!st.st_size) { + evdns_resolv_set_defaults(flags); + err = (flags & DNS_OPTION_NAMESERVERS) ? 6 : 0; + goto out1; + } + if (st.st_size > 65535) { err = 3; goto out1; } /* no resolv.conf should be any bigger */ + + resolv = (u8 *) malloc((size_t)st.st_size + 1); + if (!resolv) { err = 4; goto out1; } + + n = 0; + while ((r = read(fd, resolv+n, (size_t)st.st_size-n)) > 0) { + n += r; + if (n == st.st_size) + break; + assert(n < st.st_size); + } + if (r < 0) { err = 5; goto out2; } + resolv[n] = 0; /* we malloced an extra byte; this should be fine. */ + + start = (char *) resolv; + for (;;) { + char *const newline = strchr(start, '\n'); + if (!newline) { + resolv_conf_parse_line(start, flags); + break; + } else { + *newline = 0; + resolv_conf_parse_line(start, flags); + start = newline + 1; + } + } + + if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) { + /* no nameservers were configured. */ + evdns_nameserver_ip_add("127.0.0.1"); + err = 6; + } + if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) { + search_set_from_hostname(); + } + +out2: + free(resolv); +out1: + close(fd); + return err; +} + +#ifdef WIN32 +/* Add multiple nameservers from a space-or-comma-separated list. */ +static int +evdns_nameserver_ip_add_line(const char *ips) { + const char *addr; + char *buf; + int r; + while (*ips) { + while (ISSPACE(*ips) || *ips == ',' || *ips == '\t') + ++ips; + addr = ips; + while (ISDIGIT(*ips) || *ips == '.' || *ips == ':') + ++ips; + buf = malloc(ips-addr+1); + if (!buf) return 4; + memcpy(buf, addr, ips-addr); + buf[ips-addr] = '\0'; + r = evdns_nameserver_ip_add(buf); + free(buf); + if (r) return r; + } + return 0; +} + +typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*); + +/* Use the windows GetNetworkParams interface in iphlpapi.dll to */ +/* figure out what our nameservers are. */ +static int +load_nameservers_with_getnetworkparams(void) +{ + /* Based on MSDN examples and inspection of c-ares code. */ + FIXED_INFO *fixed; + HMODULE handle = 0; + ULONG size = sizeof(FIXED_INFO); + void *buf = NULL; + int status = 0, r, added_any; + IP_ADDR_STRING *ns; + GetNetworkParams_fn_t fn; + + if (!(handle = LoadLibrary("iphlpapi.dll"))) { + log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll"); + status = -1; + goto done; + } + if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, "GetNetworkParams"))) { + log(EVDNS_LOG_WARN, "Could not get address of function."); + status = -1; + goto done; + } + + buf = malloc(size); + if (!buf) { status = 4; goto done; } + fixed = buf; + r = fn(fixed, &size); + if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) { + status = -1; + goto done; + } + if (r != ERROR_SUCCESS) { + free(buf); + buf = malloc(size); + if (!buf) { status = 4; goto done; } + fixed = buf; + r = fn(fixed, &size); + if (r != ERROR_SUCCESS) { + log(EVDNS_LOG_DEBUG, "fn() failed."); + status = -1; + goto done; + } + } + + assert(fixed); + added_any = 0; + ns = &(fixed->DnsServerList); + while (ns) { + r = evdns_nameserver_ip_add_line(ns->IpAddress.String); + if (r) { + log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list,error: %d", + (ns->IpAddress.String),(int)GetLastError()); + status = r; + goto done; + } else { + log(EVDNS_LOG_DEBUG,"Succesfully added %s as nameserver",ns->IpAddress.String); + } + + added_any++; + ns = ns->Next; + } + + if (!added_any) { + log(EVDNS_LOG_DEBUG, "No nameservers added."); + status = -1; + } + + done: + if (buf) + free(buf); + if (handle) + FreeLibrary(handle); + return status; +} + +static int +config_nameserver_from_reg_key(HKEY key, const char *subkey) +{ + char *buf; + DWORD bufsz = 0, type = 0; + int status = 0; + + if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz) + != ERROR_MORE_DATA) + return -1; + if (!(buf = malloc(bufsz))) + return -1; + + if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz) + == ERROR_SUCCESS && bufsz > 1) { + status = evdns_nameserver_ip_add_line(buf); + } + + free(buf); + return status; +} + +#define SERVICES_KEY "System\\CurrentControlSet\\Services\\" +#define WIN_NS_9X_KEY SERVICES_KEY "VxD\\MSTCP" +#define WIN_NS_NT_KEY SERVICES_KEY "Tcpip\\Parameters" + +static int +load_nameservers_from_registry(void) +{ + int found = 0; + int r; +#define TRY(k, name) \ + if (!found && config_nameserver_from_reg_key(k,name) == 0) { \ + log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \ + found = 1; \ + } else if (!found) { \ + log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \ + #k,#name); \ + } + + if (((int)GetVersion()) > 0) { /* NT */ + HKEY nt_key = 0, interfaces_key = 0; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, + KEY_READ, &nt_key) != ERROR_SUCCESS) { + log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError()); + return -1; + } + r = RegOpenKeyEx(nt_key, "Interfaces", 0, + KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS, + &interfaces_key); + if (r != ERROR_SUCCESS) { + log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError()); + return -1; + } + TRY(nt_key, "NameServer"); + TRY(nt_key, "DhcpNameServer"); + TRY(interfaces_key, "NameServer"); + TRY(interfaces_key, "DhcpNameServer"); + RegCloseKey(interfaces_key); + RegCloseKey(nt_key); + } else { + HKEY win_key = 0; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0, + KEY_READ, &win_key) != ERROR_SUCCESS) { + log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError()); + return -1; + } + TRY(win_key, "NameServer"); + RegCloseKey(win_key); + } + + if (found == 0) { + log(EVDNS_LOG_WARN,"Didn't find any nameservers."); + } + + return found ? 0 : -1; +#undef TRY +} + +int +evdns_config_windows_nameservers(void) +{ + if (load_nameservers_with_getnetworkparams() == 0) + return 0; + return load_nameservers_from_registry(); +} +#endif + +int +evdns_init(void) +{ + int res = 0; +#ifdef WIN32 + res = evdns_config_windows_nameservers(); +#else + res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); +#endif + + return (res); +} + +const char * +evdns_err_to_string(int err) +{ + switch (err) { + case DNS_ERR_NONE: return "no error"; + case DNS_ERR_FORMAT: return "misformatted query"; + case DNS_ERR_SERVERFAILED: return "server failed"; + case DNS_ERR_NOTEXIST: return "name does not exist"; + case DNS_ERR_NOTIMPL: return "query not implemented"; + case DNS_ERR_REFUSED: return "refused"; + + case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed"; + case DNS_ERR_UNKNOWN: return "unknown"; + case DNS_ERR_TIMEOUT: return "request timed out"; + case DNS_ERR_SHUTDOWN: return "dns subsystem shut down"; + default: return "[Unknown error code]"; + } +} + +void +evdns_shutdown(int fail_requests) +{ + struct nameserver *server, *server_next; + struct search_domain *dom, *dom_next; + + while (req_head) { + if (fail_requests) + reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(req_head, &req_head); + } + while (req_waiting_head) { + if (fail_requests) + reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(req_waiting_head, &req_waiting_head); + } + global_requests_inflight = global_requests_waiting = 0; + + for (server = server_head; server; server = server_next) { + server_next = server->next; + if (server->socket >= 0) + CLOSE_SOCKET(server->socket); + (void) event_del(&server->event); + if (server->state == 0) + (void) event_del(&server->timeout_event); + free(server); + if (server_next == server_head) + break; + } + server_head = NULL; + global_good_nameservers = 0; + + if (global_search_state) { + for (dom = global_search_state->head; dom; dom = dom_next) { + dom_next = dom->next; + free(dom); + } + free(global_search_state); + global_search_state = NULL; + } + evdns_log_fn = NULL; +} + +#ifdef EVDNS_MAIN +void +main_callback(int result, char type, int count, int ttl, + void *addrs, void *orig) { + char *n = (char*)orig; + int i; + for (i = 0; i < count; ++i) { + if (type == DNS_IPv4_A) { + printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i])); + } else if (type == DNS_PTR) { + printf("%s: %s\n", n, ((char**)addrs)[i]); + } + } + if (!count) { + printf("%s: No answer (%d)\n", n, result); + } + fflush(stdout); +} +void +evdns_server_callback(struct evdns_server_request *req, void *data) +{ + int i, r; + (void)data; + /* dummy; give 192.168.11.11 as an answer for all A questions, + * give foo.bar.example.com as an answer for all PTR questions. */ + for (i = 0; i < req->nquestions; ++i) { + u32 ans = htonl(0xc0a80b0bUL); + if (req->questions[i]->type == EVDNS_TYPE_A && + 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]->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]->dns_question_class); + } + } + + r = evdns_request_respond(req, 0); + if (r<0) + printf("eeek, couldn't send reply.\n"); +} + +void +logfn(int is_warn, const char *msg) { + (void) is_warn; + fprintf(stderr, "%s\n", msg); +} +int +main(int c, char **v) { + int idx; + int reverse = 0, verbose = 1, servertest = 0; + if (c<2) { + fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]); + fprintf(stderr, "syntax: %s [-servertest]\n", v[0]); + return 1; + } + idx = 1; + while (idx < c && v[idx][0] == '-') { + if (!strcmp(v[idx], "-x")) + reverse = 1; + else if (!strcmp(v[idx], "-v")) + verbose = 1; + else if (!strcmp(v[idx], "-servertest")) + servertest = 1; + else + fprintf(stderr, "Unknown option %s\n", v[idx]); + ++idx; + } + event_init(); + if (verbose) + evdns_set_log_fn(logfn); + evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf"); + if (servertest) { + int sock; + struct sockaddr_in my_addr; + sock = socket(PF_INET, SOCK_DGRAM, 0); + 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; + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) { + perror("bind"); + exit(1); + } + evdns_add_server_port(sock, 0, evdns_server_callback, NULL); + } + for (; idx < c; ++idx) { + if (reverse) { + struct in_addr addr; + if (!inet_aton(v[idx], &addr)) { + fprintf(stderr, "Skipping non-IP %s\n", v[idx]); + continue; + } + fprintf(stderr, "resolving %s...\n",v[idx]); + evdns_resolve_reverse(&addr, 0, main_callback, v[idx]); + } else { + fprintf(stderr, "resolving (fwd) %s...\n",v[idx]); + evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]); + } + } + fflush(stdout); + event_dispatch(); + return 0; +} +#endif diff --git a/libevent/evdns.h b/libevent/evdns.h new file mode 100644 index 00000000000..1eb5c382480 --- /dev/null +++ b/libevent/evdns.h @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2006 Niels Provos + * 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. + */ + +/* + * The original DNS code is due to Adam Langley with heavy + * modifications by Nick Mathewson. Adam put his DNS software in the + * public domain. You can find his original copyright below. Please, + * aware that the code as part of libevent is governed by the 3-clause + * BSD license above. + * + * This software is Public Domain. To view a copy of the public domain dedication, + * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to + * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + * + * I ask and expect, but do not require, that all derivative works contain an + * attribution similar to: + * Parts developed by Adam Langley + * + * You may wish to replace the word "Parts" with something else depending on + * the amount of original code. + * + * (Derivative works does not include programs which link against, run or include + * 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, + * mostly stemming from the fact that the libc resolver has never been + * very good at them. Before you use this library you should see if libc + * can do the job for you with the modern async call getaddrinfo_a + * (see http://www.imperialviolet.org/page25.html#e498). Otherwise, + * please continue. + * + * This code is based on libevent and you must call event_init before + * any of the APIs in this file. You must also seed the OpenSSL random + * source if you are using OpenSSL for ids (see below). + * + * This library is designed to be included and shipped with your source + * code. You statically link with it. You should also test for the + * existence of strtok_r and define HAVE_STRTOK_R if you have it. + * + * The DNS protocol requires a good source of id numbers and these + * numbers should be unpredictable for spoofing reasons. There are + * three methods for generating them here and you must define exactly + * one of them. In increasing order of preference: + * + * DNS_USE_GETTIMEOFDAY_FOR_ID: + * Using the bottom 16 bits of the usec result from gettimeofday. This + * is a pretty poor solution but should work anywhere. + * DNS_USE_CPU_CLOCK_FOR_ID: + * Using the bottom 16 bits of the nsec result from the CPU's time + * counter. This is better, but may not work everywhere. Requires + * POSIX realtime support and you'll need to link against -lrt on + * glibc systems at least. + * DNS_USE_OPENSSL_FOR_ID: + * Uses the OpenSSL RAND_bytes call to generate the data. You must + * have seeded the pool before making any calls to this library. + * + * The library keeps track of the state of nameservers and will avoid + * them when they go down. Otherwise it will round robin between them. + * + * Quick start guide: + * #include "evdns.h" + * void callback(int result, char type, int count, int ttl, + * void *addresses, void *arg); + * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf"); + * evdns_resolve("www.hostname.com", 0, callback, NULL); + * + * When the lookup is complete the callback function is called. The + * first argument will be one of the DNS_ERR_* defines in evdns.h. + * Hopefully it will be DNS_ERR_NONE, in which case type will be + * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time + * which the data can be cached for (in seconds), addresses will point + * to an array of uint32_t's and arg will be whatever you passed to + * evdns_resolve. + * + * Searching: + * + * In order for this library to be a good replacement for glibc's resolver it + * supports searching. This involves setting a list of default domains, in + * which names will be queried for. The number of dots in the query name + * determines the order in which this list is used. + * + * Searching appears to be a single lookup from the point of view of the API, + * although many DNS queries may be generated from a single call to + * evdns_resolve. Searching can also drastically slow down the resolution + * of names. + * + * To disable searching: + * 1. Never set it up. If you never call evdns_resolv_conf_parse or + * evdns_search_add then no searching will occur. + * + * 2. If you do call evdns_resolv_conf_parse then don't pass + * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it). + * + * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag. + * + * The order of searches depends on the number of dots in the name. If the + * number is greater than the ndots setting then the names is first tried + * globally. Otherwise each search domain is appended in turn. + * + * The ndots setting can either be set from a resolv.conf, or by calling + * evdns_search_ndots_set. + * + * For example, with ndots set to 1 (the default) and a search domain list of + * ["myhome.net"]: + * Query: www + * Order: www.myhome.net, www. + * + * Query: www.abc + * Order: www.abc., www.abc.myhome.net + * + * Internals: + * + * Requests are kept in two queues. The first is the inflight queue. In + * this queue requests have an allocated transaction id and nameserver. + * They will soon be transmitted if they haven't already been. + * + * The second is the waiting queue. The size of the inflight ring is + * limited and all other requests wait in waiting queue for space. This + * bounds the number of concurrent requests so that we don't flood the + * nameserver. Several algorithms require a full walk of the inflight + * queue and so bounding its size keeps thing going nicely under huge + * (many thousands of requests) loads. + * + * If a nameserver loses too many requests it is considered down and we + * try not to use it. After a while we send a probe to that nameserver + * (a lookup for google.com) and, if it replies, we consider it working + * again. If the nameserver fails a probe we wait longer to try again + * with the next probe. + */ + +#ifndef EVENTDNS_H +#define EVENTDNS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* For integer types. */ +#include + +/** Error codes 0-5 are as described in RFC 1035. */ +#define DNS_ERR_NONE 0 +/** 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 + * name server */ +#define DNS_ERR_SERVERFAILED 2 +/** The domain name does not exist */ +#define DNS_ERR_NOTEXIST 3 +/** 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 + * reasons */ +#define DNS_ERR_REFUSED 5 +/** The reply was truncated or ill-formated */ +#define DNS_ERR_TRUNCATED 65 +/** An unknown error occurred */ +#define DNS_ERR_UNKNOWN 66 +/** Communication with the server timed out */ +#define DNS_ERR_TIMEOUT 67 +/** The request was canceled because the DNS subsystem was shut down. */ +#define DNS_ERR_SHUTDOWN 68 + +#define DNS_IPv4_A 1 +#define DNS_PTR 2 +#define DNS_IPv6_AAAA 3 + +#define DNS_QUERY_NO_SEARCH 1 + +#define DNS_OPTION_SEARCH 1 +#define DNS_OPTION_NAMESERVERS 2 +#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 + * - ttl is the number of seconds the resolution may be cached for. + * - addresses needs to be cast according to type + */ +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(const 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(const 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); + + +/** + 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 WIN32 +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 + +/* + * Structures and functions used to implement a DNS server. + */ + +struct evdns_server_request { + int flags; + int nquestions; + struct evdns_server_question **questions; +}; +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 *); +#define EVDNS_ANSWER_SECTION 0 +#define EVDNS_AUTHORITY_SECTION 1 +#define EVDNS_ADDITIONAL_SECTION 2 + +#define EVDNS_TYPE_A 1 +#define EVDNS_TYPE_NS 2 +#define EVDNS_TYPE_CNAME 5 +#define EVDNS_TYPE_SOA 6 +#define EVDNS_TYPE_PTR 12 +#define EVDNS_TYPE_MX 15 +#define EVDNS_TYPE_TXT 16 +#define EVDNS_TYPE_AAAA 28 + +#define EVDNS_QTYPE_AXFR 252 +#define EVDNS_QTYPE_ALL 255 + +#define EVDNS_CLASS_INET 1 + +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 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); +int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); + +int evdns_server_request_respond(struct evdns_server_request *req, int err); +int evdns_server_request_drop(struct evdns_server_request *req); +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 */ diff --git a/libevent/event-internal.h b/libevent/event-internal.h new file mode 100644 index 00000000000..6436b3358bd --- /dev/null +++ b/libevent/event-internal.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 _EVENT_INTERNAL_H_ +#define _EVENT_INTERNAL_H_ + +#ifdef __cplusplus +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; + int event_count; /* counts number of total events */ + 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; + int nactivequeues; + + /* signal handling info */ + struct evsignal_info sig; + + struct event_list eventqueue; + struct timeval event_tv; + + struct min_heap timeheap; + + struct timeval tv_cache; +}; + +/* Internal use only: Functions that might be missing from */ +#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) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#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 + +#endif /* _EVENT_INTERNAL_H_ */ diff --git a/libevent/event.3 b/libevent/event.3 new file mode 100644 index 00000000000..5b33ec64a93 --- /dev/null +++ b/libevent/event.3 @@ -0,0 +1,624 @@ +.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $ +.\" +.\" Copyright (c) 2000 Artur Grabowski +.\" 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 ``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. +.\" +.Dd August 8, 2000 +.Dt EVENT 3 +.Os +.Sh NAME +.Nm event_init , +.Nm event_dispatch , +.Nm event_loop , +.Nm event_loopexit , +.Nm event_loopbreak , +.Nm event_set , +.Nm event_base_dispatch , +.Nm event_base_loop , +.Nm event_base_loopexit , +.Nm event_base_loopbreak , +.Nm event_base_set , +.Nm event_base_free , +.Nm event_add , +.Nm event_del , +.Nm event_once , +.Nm event_base_once , +.Nm event_pending , +.Nm event_initialized , +.Nm event_priority_init , +.Nm event_priority_set , +.Nm evtimer_set , +.Nm evtimer_add , +.Nm evtimer_del , +.Nm evtimer_pending , +.Nm evtimer_initialized , +.Nm signal_set , +.Nm signal_add , +.Nm signal_del , +.Nm signal_pending , +.Nm signal_initialized , +.Nm bufferevent_new , +.Nm bufferevent_free , +.Nm bufferevent_write , +.Nm bufferevent_write_buffer , +.Nm bufferevent_read , +.Nm bufferevent_enable , +.Nm bufferevent_disable , +.Nm bufferevent_settimeout , +.Nm bufferevent_base_set , +.Nm evbuffer_new , +.Nm evbuffer_free , +.Nm evbuffer_add , +.Nm evbuffer_add_buffer , +.Nm evbuffer_add_printf , +.Nm evbuffer_add_vprintf , +.Nm evbuffer_drain , +.Nm evbuffer_write , +.Nm evbuffer_read , +.Nm evbuffer_find , +.Nm evbuffer_readline , +.Nm evhttp_new , +.Nm evhttp_bind_socket , +.Nm evhttp_free +.Nd execute a function when a specific event occurs +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Ft "struct event_base *" +.Fn "event_init" "void" +.Ft int +.Fn "event_dispatch" "void" +.Ft int +.Fn "event_loop" "int flags" +.Ft int +.Fn "event_loopexit" "struct timeval *tv" +.Ft int +.Fn "event_loopbreak" "void" +.Ft void +.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" +.Ft int +.Fn "event_base_dispatch" "struct event_base *base" +.Ft int +.Fn "event_base_loop" "struct event_base *base" "int flags" +.Ft int +.Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv" +.Ft int +.Fn "event_base_loopbreak" "struct event_base *base" +.Ft int +.Fn "event_base_set" "struct event_base *base" "struct event *" +.Ft void +.Fn "event_base_free" "struct event_base *base" +.Ft int +.Fn "event_add" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "event_del" "struct event *ev" +.Ft int +.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv" +.Ft int +.Fn "event_base_once" "struct event_base *base" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv" +.Ft int +.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv" +.Ft int +.Fn "event_initialized" "struct event *ev" +.Ft int +.Fn "event_priority_init" "int npriorities" +.Ft int +.Fn "event_priority_set" "struct event *ev" "int priority" +.Ft void +.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg" +.Ft void +.Fn "evtimer_add" "struct event *ev" "struct timeval *" +.Ft void +.Fn "evtimer_del" "struct event *ev" +.Ft int +.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "evtimer_initialized" "struct event *ev" +.Ft void +.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg" +.Ft void +.Fn "signal_add" "struct event *ev" "struct timeval *" +.Ft void +.Fn "signal_del" "struct event *ev" +.Ft int +.Fn "signal_pending" "struct event *ev" "struct timeval *tv" +.Ft int +.Fn "signal_initialized" "struct event *ev" +.Ft "struct bufferevent *" +.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg" +.Ft void +.Fn "bufferevent_free" "struct bufferevent *bufev" +.Ft int +.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size" +.Ft int +.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf" +.Ft size_t +.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size" +.Ft int +.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event" +.Ft int +.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event" +.Ft void +.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write" +.Ft int +.Fn "bufferevent_base_set" "struct event_base *base" "struct bufferevent *bufev" +.Ft "struct evbuffer *" +.Fn "evbuffer_new" "void" +.Ft void +.Fn "evbuffer_free" "struct evbuffer *buf" +.Ft int +.Fn "evbuffer_add" "struct evbuffer *buf" "const void *data" "size_t size" +.Ft int +.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src" +.Ft int +.Fn "evbuffer_add_printf" "struct evbuffer *buf" "const char *fmt" "..." +.Ft int +.Fn "evbuffer_add_vprintf" "struct evbuffer *buf" "const char *fmt" "va_list ap" +.Ft void +.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size" +.Ft int +.Fn "evbuffer_write" "struct evbuffer *buf" "int fd" +.Ft int +.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size" +.Ft "u_char *" +.Fn "evbuffer_find" "struct evbuffer *buf" "const u_char *data" "size_t size" +.Ft "char *" +.Fn "evbuffer_readline" "struct evbuffer *buf" +.Ft "struct evhttp *" +.Fn "evhttp_new" "struct event_base *base" +.Ft int +.Fn "evhttp_bind_socket" "struct evhttp *http" "const char *address" "u_short port" +.Ft "void" +.Fn "evhttp_free" "struct evhttp *http" +.Ft int +.Fa (*event_sigcb)(void) ; +.Ft volatile sig_atomic_t +.Fa event_gotsig ; +.Sh DESCRIPTION +The +.Nm event +API provides a mechanism to execute a function when a specific event +on a file descriptor occurs or after a given time has passed. +.Pp +The +.Nm event +API needs to be initialized with +.Fn event_init +before it can be used. +.Pp +In order to process events, an application needs to call +.Fn event_dispatch . +This function only returns on error, and should replace the event core +of the application program. +.Pp +The function +.Fn event_set +prepares the event structure +.Fa ev +to be used in future calls to +.Fn event_add +and +.Fn event_del . +The event will be prepared to call the function specified by the +.Fa fn +argument with an +.Fa int +argument indicating the file descriptor, a +.Fa short +argument indicating the type of event, and a +.Fa void * +argument given in the +.Fa arg +argument. +The +.Fa fd +indicates the file descriptor that should be monitored for events. +The events can be either +.Va EV_READ , +.Va EV_WRITE , +or both, +indicating that an application can read or write from the file descriptor +respectively without blocking. +.Pp +The function +.Fa fn +will be called with the file descriptor that triggered the event and +the type of event which will be either +.Va EV_TIMEOUT , +.Va EV_SIGNAL , +.Va EV_READ , +or +.Va EV_WRITE . +Additionally, an event which has registered interest in more than one of the +preceeding events, via bitwise-OR to +.Fn event_set , +can provide its callback function with a bitwise-OR of more than one triggered +event. +The additional flag +.Va EV_PERSIST +makes an +.Fn event_add +persistent until +.Fn event_del +has been called. +.Pp +Once initialized, the +.Fa ev +structure can be used repeatedly with +.Fn event_add +and +.Fn event_del +and does not need to be reinitialized unless the function called and/or +the argument to it are to be changed. +However, when an +.Fa ev +structure has been added to libevent using +.Fn event_add +the structure must persist until the event occurs (assuming +.Fa EV_PERSIST +is not set) or is removed +using +.Fn event_del . +You may not reuse the same +.Fa ev +structure for multiple monitored descriptors; each descriptor +needs its own +.Fa ev . +.Pp +The function +.Fn event_add +schedules the execution of the +.Fa ev +event when the event specified in +.Fn event_set +occurs or in at least the time specified in the +.Fa tv . +If +.Fa tv +is +.Dv NULL , +no timeout occurs and the function will only be called +if a matching event occurs on the file descriptor. +The event in the +.Fa ev +argument must be already initialized by +.Fn event_set +and may not be used in calls to +.Fn event_set +until it has timed out or been removed with +.Fn event_del . +If the event in the +.Fa ev +argument already has a scheduled timeout, the old timeout will be +replaced by the new one. +.Pp +The function +.Fn event_del +will cancel the event in the argument +.Fa ev . +If the event has already executed or has never been added +the call will have no effect. +.Pp +The functions +.Fn evtimer_set , +.Fn evtimer_add , +.Fn evtimer_del , +.Fn evtimer_initialized , +and +.Fn evtimer_pending +are abbreviations for common situations where only a timeout is required. +The file descriptor passed will be \-1, and the event type will be +.Va EV_TIMEOUT . +.Pp +The functions +.Fn signal_set , +.Fn signal_add , +.Fn signal_del , +.Fn signal_initialized , +and +.Fn signal_pending +are abbreviations. +The event type will be a persistent +.Va EV_SIGNAL . +That means +.Fn signal_set +adds +.Va EV_PERSIST . +.Pp +In order to avoid races in signal handlers, the +.Nm event +API provides two variables: +.Va event_sigcb +and +.Va event_gotsig . +A signal handler +sets +.Va event_gotsig +to indicate that a signal has been received. +The application sets +.Va event_sigcb +to a callback function. +After the signal handler sets +.Va event_gotsig , +.Nm event_dispatch +will execute the callback function to process received signals. +The callback returns 1 when no events are registered any more. +It can return \-1 to indicate an error to the +.Nm event +library, causing +.Fn event_dispatch +to terminate with +.Va errno +set to +.Er EINTR . +.Pp +The function +.Fn event_once +is similar to +.Fn event_set . +However, it schedules a callback to be called exactly once and does not +require the caller to prepare an +.Fa event +structure. +This function supports +.Fa EV_TIMEOUT , +.Fa EV_READ , +and +.Fa EV_WRITE . +.Pp +The +.Fn event_pending +function can be used to check if the event specified by +.Fa event +is pending to run. +If +.Va EV_TIMEOUT +was specified and +.Fa tv +is not +.Dv NULL , +the expiration time of the event will be returned in +.Fa tv . +.Pp +The +.Fn event_initialized +macro can be used to check if an event has been initialized. +.Pp +The +.Nm event_loop +function provides an interface for single pass execution of pending +events. +The flags +.Va EVLOOP_ONCE +and +.Va EVLOOP_NONBLOCK +are recognized. +The +.Nm event_loopexit +function exits from the event loop. The next +.Fn 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 +.Fn event_loop +will proceed normally. +The +.Nm event_loopbreak +function exits from the event loop immediately. +.Fn event_loop +will abort after the next event is completed; +.Fn event_loopbreak +is typically invoked from this event's callback. This behavior is analogous +to the "break;" statement. Subsequent invocations of +.Fn event_loop +will proceed normally. +.Pp +It is the responsibility of the caller to provide these functions with +pre-allocated event structures. +.Pp +.Sh EVENT PRIORITIES +By default +.Nm libevent +schedules all active events with the same priority. +However, sometimes it is desirable to process some events with a higher +priority than others. +For that reason, +.Nm libevent +supports strict priority queues. +Active events with a lower priority are always processed before events +with a higher priority. +.Pp +The number of different priorities can be set initially with the +.Fn event_priority_init +function. +This function should be called before the first call to +.Fn event_dispatch . +The +.Fn event_priority_set +function can be used to assign a priority to an event. +By default, +.Nm libevent +assigns the middle priority to all events unless their priority +is explicitly set. +.Sh THREAD SAFE EVENTS +.Nm Libevent +has experimental support for thread-safe events. +When initializing the library via +.Fn event_init , +an event base is returned. +This event base can be used in conjunction with calls to +.Fn event_base_set , +.Fn event_base_dispatch , +.Fn event_base_loop , +.Fn event_base_loopexit , +.Fn bufferevent_base_set +and +.Fn event_base_free . +.Fn event_base_set +should be called after preparing an event with +.Fn event_set , +as +.Fn event_set +assigns the provided event to the most recently created event base. +.Fn bufferevent_base_set +should be called after preparing a bufferevent with +.Fn bufferevent_new . +.Fn event_base_free +should be used to free memory associated with the event base +when it is no longer needed. +.Sh BUFFERED EVENTS +.Nm libevent +provides an abstraction on top of the regular event callbacks. +This abstraction is called a +.Va "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 IO, +but instead is reading from input and writing to output buffers. +.Pp +A new bufferevent is created by +.Fn bufferevent_new . +The parameter +.Fa fd +specifies the file descriptor from which data is read and written to. +This file descriptor is not allowed to be a +.Xr pipe 2 . +The next three parameters are callbacks. +The read and write callback have the following form: +.Ft void +.Fn "(*cb)" "struct bufferevent *bufev" "void *arg" . +The error callback has the following form: +.Ft void +.Fn "(*cb)" "struct bufferevent *bufev" "short what" "void *arg" . +The argument is specified by the fourth parameter +.Fa "cbarg" . +A +.Fa bufferevent struct +pointer is returned on success, NULL on error. +Both the read and the write callback may be NULL. +The error callback has to be always provided. +.Pp +Once initialized, the bufferevent structure can be used repeatedly with +bufferevent_enable() and bufferevent_disable(). +The flags parameter can be a combination of +.Va EV_READ +and +.Va EV_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 +.Va 0 +by default. +.Pp +The +.Fn 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. +.Fn bufferevent_write +returns 0 on success or \-1 on failure. +The +.Fn bufferevent_read +function is used to read data from the input buffer, +returning the amount of data read. +.Pp +If multiple bases are in use, bufferevent_base_set() must be called before +enabling the bufferevent for the first time. +.Sh NON-BLOCKING HTTP SUPPORT +.Nm libevent +provides a very thin HTTP layer that can be used both to host an HTTP +server and also to make HTTP requests. +An HTTP server can be created by calling +.Fn evhttp_new . +It can be bound to any port and address with the +.Fn evhttp_bind_socket +function. +When the HTTP server is no longer used, it can be freed via +.Fn evhttp_free . +.Pp +To be notified of HTTP requests, a user needs to register callbacks with the +HTTP server. +This can be done by calling +.Fn evhttp_set_cb . +The second argument is the URI for which a callback is being registered. +The corresponding callback will receive an +.Va struct evhttp_request +object that contains all information about the request. +.Pp +This section does not document all the possible function calls; please +check +.Va event.h +for the public interfaces. +.Sh ADDITIONAL NOTES +It is possible to disable support for +.Va epoll , kqueue , devpoll , poll +or +.Va select +by setting the environment variable +.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NODEVPOLL , EVENT_NOPOLL +or +.Va EVENT_NOSELECT , +respectively. +By setting the environment variable +.Va EVENT_SHOW_METHOD , +.Nm libevent +displays the kernel notification method that it uses. +.Sh RETURN VALUES +Upon successful completion +.Fn event_add +and +.Fn event_del +return 0. +Otherwise, \-1 is returned and the global variable errno is +set to indicate the error. +.Sh SEE ALSO +.Xr kqueue 2 , +.Xr poll 2 , +.Xr select 2 , +.Xr evdns 3 , +.Xr timeout 9 +.Sh HISTORY +The +.Nm event +API manpage is based on the +.Xr timeout 9 +manpage by Artur Grabowski. +The port of +.Nm libevent +to Windows is due to Michael A. Davis. +Support for real-time signals is due to Taral. +.Sh AUTHORS +The +.Nm event +library was written by Niels Provos. +.Sh BUGS +This documentation is neither complete nor authoritative. +If you are in doubt about the usage of this API then +check the source code to find out how it works, write +up the missing piece of documentation and send it to +me for inclusion in this man page. diff --git a/libevent/event.c b/libevent/event.c new file mode 100644 index 00000000000..6eb5db05c87 --- /dev/null +++ b/libevent/event.c @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 +#undef WIN32_LEAN_AND_MEAN +#endif +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#include +#include +#include + +#include "event.h" +#include "event-internal.h" +#include "evutil.h" +#include "log.h" + +#ifdef HAVE_EVENT_PORTS +extern const struct eventop evportops; +#endif +#ifdef HAVE_SELECT +extern const struct eventop selectops; +#endif +#ifdef HAVE_POLL +extern const struct eventop pollops; +#endif +#ifdef HAVE_EPOLL +extern const struct eventop epollops; +#endif +#ifdef HAVE_WORKING_KQUEUE +extern const struct eventop kqops; +#endif +#ifdef HAVE_DEVPOLL +extern const struct eventop devpollops; +#endif +#ifdef WIN32 +extern const struct eventop win32ops; +#endif + +/* In order of preference */ +static const struct eventop *eventops[] = { +#ifdef HAVE_EVENT_PORTS + &evportops, +#endif +#ifdef HAVE_WORKING_KQUEUE + &kqops, +#endif +#ifdef HAVE_EPOLL + &epollops, +#endif +#ifdef HAVE_DEVPOLL + &devpollops, +#endif +#ifdef HAVE_POLL + &pollops, +#endif +#ifdef HAVE_SELECT + &selectops, +#endif +#ifdef WIN32 + &win32ops, +#endif + NULL +}; + +/* Global state */ +struct event_base *current_base = NULL; +extern struct event_base *evsignal_base; +static int use_monotonic; + +/* Handle signals - This is a deprecated interface */ +int (*event_sigcb)(void); /* Signal callback when gotsig is set */ +volatile sig_atomic_t event_gotsig; /* Set in signal handler */ + +/* Prototypes */ +static void event_queue_insert(struct event_base *, struct event *, int); +static void event_queue_remove(struct event_base *, struct event *, int); +static int event_haveevents(struct event_base *); + +static void event_process_active(struct event_base *); + +static int timeout_next(struct event_base *, struct timeval **); +static void timeout_process(struct event_base *); +static void timeout_correct(struct event_base *, struct timeval *); + +static void +detect_monotonic(void) +{ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + use_monotonic = 1; +#endif +} + +static int +gettime(struct event_base *base, struct timeval *tp) +{ + if (base->tv_cache.tv_sec) { + *tp = base->tv_cache; + return (0); + } + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + if (use_monotonic) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + return (-1); + + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; + return (0); + } +#endif + + return (evutil_gettimeofday(tp, NULL)); +} + +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", __func__); + + event_sigcb = NULL; + event_gotsig = 0; + + detect_monotonic(); + gettime(base, &base->event_tv); + + min_heap_ctor(&base->timeheap); + TAILQ_INIT(&base->eventqueue); + base->sig.ev_signal_pair[0] = -1; + base->sig.ev_signal_pair[1] = -1; + + base->evbase = NULL; + for (i = 0; eventops[i] && !base->evbase; i++) { + base->evsel = eventops[i]; + + base->evbase = base->evsel->init(base); + } + + if (base->evbase == NULL) + event_errx(1, "%s: no event mechanism available", __func__); + + if (getenv("EVENT_SHOW_METHOD")) + event_msgx("libevent using: %s\n", + base->evsel->name); + + /* allocate a single active event queue */ + event_base_priority_init(base, 1); + + return (base); +} + +void +event_base_free(struct event_base *base) +{ + int i, n_deleted=0; + struct event *ev; + + if (base == NULL && current_base) + 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; + } + + for (i = 0; i < base->nactivequeues; ++i) { + for (ev = TAILQ_FIRST(base->activequeues[i]); ev; ) { + struct event *next = TAILQ_NEXT(ev, ev_active_next); + if (!(ev->ev_flags & EVLIST_INTERNAL)) { + event_del(ev); + ++n_deleted; + } + ev = next; + } + } + + 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) + assert(TAILQ_EMPTY(base->activequeues[i])); + + assert(min_heap_empty(&base->timeheap)); + min_heap_dtor(&base->timeheap); + + for (i = 0; i < base->nactivequeues; ++i) + free(base->activequeues[i]); + free(base->activequeues); + + assert(TAILQ_EMPTY(&base->eventqueue)); + + 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); + + /* prevent internal delete */ + if (base->sig.ev_signal_added) { + /* we cannot call event_del here because the base has + * not been reinitialized yet. */ + event_queue_remove(base, &base->sig.ev_signal, + EVLIST_INSERTED); + if (base->sig.ev_signal.ev_flags & EVLIST_ACTIVE) + event_queue_remove(base, &base->sig.ev_signal, + EVLIST_ACTIVE); + base->sig.ev_signal_added = 0; + } + + if (base->evsel->dealloc != NULL) + base->evsel->dealloc(base, base->evbase); + 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) +{ + return event_base_priority_init(current_base, npriorities); +} + +int +event_base_priority_init(struct event_base *base, int npriorities) +{ + int i; + + if (base->event_count_active) + return (-1); + + if (base->nactivequeues && npriorities != base->nactivequeues) { + for (i = 0; i < base->nactivequeues; ++i) { + free(base->activequeues[i]); + } + free(base->activequeues); + } + + /* Allocate our priority queues */ + base->nactivequeues = npriorities; + base->activequeues = (struct event_list **)calloc(base->nactivequeues, + npriorities * sizeof(struct event_list *)); + if (base->activequeues == NULL) + event_err(1, "%s: calloc", __func__); + + for (i = 0; i < base->nactivequeues; ++i) { + base->activequeues[i] = malloc(sizeof(struct event_list)); + if (base->activequeues[i] == NULL) + event_err(1, "%s: malloc", __func__); + TAILQ_INIT(base->activequeues[i]); + } + + return (0); +} + +int +event_haveevents(struct event_base *base) +{ + return (base->event_count > 0); +} + +/* + * Active events are stored in priority queues. Lower priorities are always + * process before higher priorities. Low priority events can starve high + * priority ones. + */ + +static void +event_process_active(struct event_base *base) +{ + struct event *ev; + struct event_list *activeq = NULL; + int i; + short ncalls; + + for (i = 0; i < base->nactivequeues; ++i) { + if (TAILQ_FIRST(base->activequeues[i]) != NULL) { + activeq = base->activequeues[i]; + break; + } + } + + assert(activeq != NULL); + + for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { + 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; + ev->ev_pncalls = &ncalls; + while (ncalls) { + ncalls--; + ev->ev_ncalls = ncalls; + (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); + if (event_gotsig || base->event_break) + return; + } + } +} + +/* + * Wait continously for events. We exit only if no events are left. + */ + +int +event_dispatch(void) +{ + return (event_loop(0)); +} + +int +event_base_dispatch(struct event_base *event_base) +{ + 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) +{ + struct event_base *base = arg; + base->event_gotterm = 1; +} + +/* not thread safe */ +int +event_loopexit(const struct timeval *tv) +{ + return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, + current_base, tv)); +} + +int +event_base_loopexit(struct event_base *event_base, const struct timeval *tv) +{ + return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb, + event_base, tv)); +} + +/* 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) +{ + return event_base_loop(current_base, flags); +} + +int +event_base_loop(struct event_base *base, int flags) +{ + const struct eventop *evsel = base->evsel; + void *evbase = base->evbase; + struct timeval tv; + struct timeval *tv_p; + int res, done; + + /* clear time cache */ + base->tv_cache.tv_sec = 0; + + if (base->sig.ev_signal_added) + evsignal_base = base; + done = 0; + while (!done) { + /* 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; + if (event_sigcb) { + res = (*event_sigcb)(); + if (res == -1) { + errno = EINTR; + return (-1); + } + } + } + + timeout_correct(base, &tv); + + tv_p = &tv; + if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { + timeout_next(base, &tv_p); + } else { + /* + * if we have active events, we just poll new events + * without waiting. + */ + evutil_timerclear(&tv); + } + + /* If we have no events, we just exit */ + if (!event_haveevents(base)) { + event_debug(("%s: no events registered.", __func__)); + return (1); + } + + /* update last old time */ + gettime(base, &base->event_tv); + + /* clear time cache */ + base->tv_cache.tv_sec = 0; + + res = evsel->dispatch(base, evbase, tv_p); + + if (res == -1) + return (-1); + gettime(base, &base->tv_cache); + + timeout_process(base); + + if (base->event_count_active) { + event_process_active(base); + if (!base->event_count_active && (flags & EVLOOP_ONCE)) + done = 1; + } else if (flags & EVLOOP_NONBLOCK) + done = 1; + } + + /* clear time cache */ + base->tv_cache.tv_sec = 0; + + event_debug(("%s: asked to terminate loop.", __func__)); + return (0); +} + +/* Sets up an event for processing once */ + +struct event_once { + struct event ev; + + void (*cb)(int, short, void *); + void *arg; +}; + +/* One-time callback, it deletes itself */ + +static void +event_once_cb(int fd, short events, void *arg) +{ + struct event_once *eonce = arg; + + (*eonce->cb)(fd, events, eonce->arg); + free(eonce); +} + +/* not threadsafe, event scheduled once. */ +int +event_once(int fd, short events, + void (*callback)(int, short, void *), void *arg, const struct timeval *tv) +{ + return event_base_once(current_base, fd, events, callback, arg, tv); +} + +/* Schedules an event once */ +int +event_base_once(struct event_base *base, int fd, short events, + void (*callback)(int, short, void *), void *arg, const struct timeval *tv) +{ + struct event_once *eonce; + struct timeval etv; + int res; + + /* We cannot support signals that just fire once */ + if (events & EV_SIGNAL) + return (-1); + + if ((eonce = calloc(1, sizeof(struct event_once))) == NULL) + return (-1); + + eonce->cb = callback; + eonce->arg = arg; + + if (events == EV_TIMEOUT) { + if (tv == NULL) { + evutil_timerclear(&etv); + tv = &etv; + } + + evtimer_set(&eonce->ev, event_once_cb, eonce); + } else if (events & (EV_READ|EV_WRITE)) { + events &= EV_READ|EV_WRITE; + + event_set(&eonce->ev, fd, events, event_once_cb, eonce); + } else { + /* Bad event combination */ + free(eonce); + return (-1); + } + + res = event_base_set(base, &eonce->ev); + if (res == 0) + res = event_add(&eonce->ev, tv); + if (res != 0) { + free(eonce); + return (res); + } + + return (0); +} + +void +event_set(struct event *ev, int fd, short events, + void (*callback)(int, short, void *), void *arg) +{ + /* Take the current base - caller needs to set the real base later */ + ev->ev_base = current_base; + + ev->ev_callback = callback; + ev->ev_arg = arg; + ev->ev_fd = fd; + ev->ev_events = events; + ev->ev_res = 0; + ev->ev_flags = EVLIST_INIT; + 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; +} + +int +event_base_set(struct event_base *base, struct event *ev) +{ + /* Only innocent events may be assigned to a different base */ + if (ev->ev_flags != EVLIST_INIT) + return (-1); + + ev->ev_base = base; + ev->ev_pri = base->nactivequeues/2; + + return (0); +} + +/* + * Set's the priority of an event - if an event is already scheduled + * changing the priority is going to fail. + */ + +int +event_priority_set(struct event *ev, int pri) +{ + if (ev->ev_flags & EVLIST_ACTIVE) + return (-1); + if (pri < 0 || pri >= ev->ev_base->nactivequeues) + return (-1); + + ev->ev_pri = pri; + + return (0); +} + +/* + * Checks if a specific event is pending or scheduled. + */ + +int +event_pending(struct event *ev, short event, struct timeval *tv) +{ + struct timeval now, res; + int flags = 0; + + if (ev->ev_flags & EVLIST_INSERTED) + flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)); + if (ev->ev_flags & EVLIST_ACTIVE) + flags |= ev->ev_res; + if (ev->ev_flags & EVLIST_TIMEOUT) + flags |= EV_TIMEOUT; + + event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL); + + /* See if there is a timeout that we should report */ + if (tv != NULL && (flags & event & EV_TIMEOUT)) { + gettime(ev->ev_base, &now); + evutil_timersub(&ev->ev_timeout, &now, &res); + /* correctly remap to real time */ + evutil_gettimeofday(&now, NULL); + evutil_timeradd(&now, &res, tv); + } + + return (flags & event); +} + +int +event_add(struct event *ev, const struct timeval *tv) +{ + struct event_base *base = ev->ev_base; + const struct eventop *evsel = base->evsel; + void *evbase = base->evbase; + int res = 0; + + event_debug(( + "event_add: event: %p, %s%s%scall %p", + ev, + ev->ev_events & EV_READ ? "EV_READ " : " ", + ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", + tv ? "EV_TIMEOUT " : " ", + ev->ev_callback)); + + assert(!(ev->ev_flags & ~EVLIST_ALL)); + + /* + * prepare for timeout insertion further below, if we get a + * failure on any step, we should not change any state. + */ + if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { + if (min_heap_reserve(&base->timeheap, + 1 + min_heap_size(&base->timeheap)) == -1) + return (-1); /* ENOMEM == errno */ + } + + if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && + !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { + res = evsel->add(evbase, ev); + if (res != -1) + event_queue_insert(base, ev, EVLIST_INSERTED); + } + + /* + * we should change the timout state only if the previous event + * addition succeeded. + */ + if (res != -1 && tv != NULL) { + struct timeval now; + + /* + * we already reserved memory above for the case where we + * are not replacing an exisiting timeout. + */ + if (ev->ev_flags & EVLIST_TIMEOUT) + event_queue_remove(base, ev, EVLIST_TIMEOUT); + + /* Check if it is active due to a timeout. Rescheduling + * this timeout before the callback can be executed + * removes it from the active list. */ + if ((ev->ev_flags & EVLIST_ACTIVE) && + (ev->ev_res & EV_TIMEOUT)) { + /* See if we are just active executing this + * event in a loop + */ + if (ev->ev_ncalls && ev->ev_pncalls) { + /* Abort loop */ + *ev->ev_pncalls = 0; + } + + event_queue_remove(base, ev, EVLIST_ACTIVE); + } + + gettime(base, &now); + evutil_timeradd(&now, tv, &ev->ev_timeout); + + event_debug(( + "event_add: timeout in %ld seconds, call %p", + tv->tv_sec, ev->ev_callback)); + + event_queue_insert(base, ev, EVLIST_TIMEOUT); + } + + return (res); +} + +int +event_del(struct event *ev) +{ + struct event_base *base; + const struct eventop *evsel; + void *evbase; + + event_debug(("event_del: %p, callback %p", + ev, ev->ev_callback)); + + /* An event without a base has not been added */ + if (ev->ev_base == NULL) + return (-1); + + base = ev->ev_base; + evsel = base->evsel; + evbase = base->evbase; + + assert(!(ev->ev_flags & ~EVLIST_ALL)); + + /* See if we are just active executing this event in a loop */ + if (ev->ev_ncalls && ev->ev_pncalls) { + /* Abort loop */ + *ev->ev_pncalls = 0; + } + + if (ev->ev_flags & EVLIST_TIMEOUT) + event_queue_remove(base, ev, EVLIST_TIMEOUT); + + if (ev->ev_flags & EVLIST_ACTIVE) + event_queue_remove(base, ev, EVLIST_ACTIVE); + + if (ev->ev_flags & EVLIST_INSERTED) { + event_queue_remove(base, ev, EVLIST_INSERTED); + return (evsel->del(evbase, ev)); + } + + return (0); +} + +void +event_active(struct event *ev, int res, short ncalls) +{ + /* We get different kinds of events, add them together */ + if (ev->ev_flags & EVLIST_ACTIVE) { + ev->ev_res |= res; + return; + } + + ev->ev_res = res; + ev->ev_ncalls = ncalls; + ev->ev_pncalls = NULL; + event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE); +} + +static int +timeout_next(struct event_base *base, struct timeval **tv_p) +{ + struct timeval now; + struct event *ev; + struct timeval *tv = *tv_p; + + if ((ev = min_heap_top(&base->timeheap)) == NULL) { + /* if no time-based events are active wait for I/O */ + *tv_p = NULL; + return (0); + } + + if (gettime(base, &now) == -1) + return (-1); + + if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { + evutil_timerclear(tv); + return (0); + } + + evutil_timersub(&ev->ev_timeout, &now, tv); + + assert(tv->tv_sec >= 0); + assert(tv->tv_usec >= 0); + + event_debug(("timeout_next: in %ld seconds", tv->tv_sec)); + return (0); +} + +/* + * Determines if the time is running backwards by comparing the current + * time against the last time we checked. Not needed when using clock + * monotonic. + */ + +static void +timeout_correct(struct event_base *base, struct timeval *tv) +{ + struct event **pev; + unsigned int size; + struct timeval off; + + if (use_monotonic) + return; + + /* Check if time is running backwards */ + gettime(base, tv); + if (evutil_timercmp(tv, &base->event_tv, >=)) { + base->event_tv = *tv; + return; + } + + event_debug(("%s: time is running backwards, corrected", + __func__)); + 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. + */ + 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); + } + /* Now remember what the new time turned out to be. */ + base->event_tv = *tv; +} + +void +timeout_process(struct event_base *base) +{ + struct timeval now; + struct event *ev; + + if (min_heap_empty(&base->timeheap)) + return; + + gettime(base, &now); + + while ((ev = min_heap_top(&base->timeheap))) { + if (evutil_timercmp(&ev->ev_timeout, &now, >)) + break; + + /* delete this event from the I/O queues */ + event_del(ev); + + event_debug(("timeout_process: call %p", + ev->ev_callback)); + event_active(ev, EV_TIMEOUT, 1); + } +} + +void +event_queue_remove(struct event_base *base, struct event *ev, int queue) +{ + 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) + base->event_count--; + + ev->ev_flags &= ~queue; + switch (queue) { + case EVLIST_INSERTED: + TAILQ_REMOVE(&base->eventqueue, ev, ev_next); + break; + case EVLIST_ACTIVE: + base->event_count_active--; + TAILQ_REMOVE(base->activequeues[ev->ev_pri], + ev, ev_active_next); + break; + case EVLIST_TIMEOUT: + min_heap_erase(&base->timeheap, ev); + break; + default: + event_errx(1, "%s: unknown queue %x", __func__, queue); + } +} + +void +event_queue_insert(struct event_base *base, struct event *ev, int queue) +{ + if (ev->ev_flags & queue) { + /* Double insertion is possible for active events */ + if (queue & EVLIST_ACTIVE) + return; + + event_errx(1, "%s: %p(fd %d) already on queue %x", __func__, + ev, ev->ev_fd, queue); + } + + if (~ev->ev_flags & EVLIST_INTERNAL) + base->event_count++; + + ev->ev_flags |= queue; + switch (queue) { + case EVLIST_INSERTED: + TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); + break; + case EVLIST_ACTIVE: + base->event_count_active++; + TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], + ev,ev_active_next); + break; + case EVLIST_TIMEOUT: { + min_heap_push(&base->timeheap, ev); + break; + } + default: + event_errx(1, "%s: unknown queue %x", __func__, queue); + } +} + +/* Functions for debugging */ + +const char * +event_get_version(void) +{ + return (VERSION); +} + +/* + * No thread-safe interface needed - the information should be the same + * for all threads. + */ + +const char * +event_get_method(void) +{ + return (current_base->evsel->name); +} diff --git a/libevent/event.h b/libevent/event.h new file mode 100644 index 00000000000..039e4f88bcb --- /dev/null +++ b/libevent/event.h @@ -0,0 +1,1175 @@ +/* + * Copyright (c) 2000-2007 Niels Provos + * 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 _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 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 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 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 +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include + +/* For int types. */ +#include + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +typedef unsigned char u_char; +typedef unsigned short u_short; +#endif + +#define EVLIST_TIMEOUT 0x01 +#define EVLIST_INSERTED 0x02 +#define EVLIST_SIGNAL 0x04 +#define EVLIST_ACTIVE 0x08 +#define EVLIST_INTERNAL 0x10 +#define EVLIST_INIT 0x80 + +/* EVLIST_X_ Private space: 0x1000-0xf000 */ +#define EVLIST_ALL (0xf000 | 0x9f) + +#define EV_TIMEOUT 0x01 +#define EV_READ 0x02 +#define EV_WRITE 0x04 +#define EV_SIGNAL 0x08 +#define EV_PERSIST 0x10 /* Persistant event */ + +/* Fix so that ppl dont have to run with */ +#ifndef TAILQ_ENTRY +#define _EVENT_DEFINED_TQENTRY +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} +#endif /* !TAILQ_ENTRY */ + +struct event_base; +struct event { + TAILQ_ENTRY (event) ev_next; + TAILQ_ENTRY (event) ev_active_next; + TAILQ_ENTRY (event) ev_signal_next; + unsigned int min_heap_idx; /* for managing timeouts */ + + struct event_base *ev_base; + + int ev_fd; + short ev_events; + short ev_ncalls; + short *ev_pncalls; /* Allows deletes in callback */ + + struct timeval ev_timeout; + + int ev_pri; /* smaller numbers are higher priority */ + + void (*ev_callback)(int, short, void *arg); + void *ev_arg; + + int ev_res; /* result passed to event callback */ + int ev_flags; +}; + +#define EVENT_SIGNAL(ev) (int)(ev)->ev_fd +#define EVENT_FD(ev) (int)(ev)->ev_fd + +/* + * Key-Value pairs. Can be used for HTTP headers but also for + * query argument parsing. + */ +struct evkeyval { + TAILQ_ENTRY(evkeyval) next; + + char *key; + char *value; +}; + +#ifdef _EVENT_DEFINED_TQENTRY +#undef TAILQ_ENTRY +struct event_list; +struct evkeyvalq; +#undef _EVENT_DEFINED_TQENTRY +#else +TAILQ_HEAD (event_list, event); +TAILQ_HEAD (evkeyvalq, evkeyval); +#endif /* _EVENT_DEFINED_TQENTRY */ + +/** + 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. + + @param eb the event base + @param ev the event + */ +int event_base_set(struct event_base *, struct event *); + +/** + 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); + +/** + 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(const 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 *, const 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) + +#define signal_add(ev, tv) event_add(ev, tv) +#define signal_set(ev, x, cb, arg) \ + event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg) +#define signal_del(ev) event_del(ev) +#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 *, + const 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 *base, int fd, short events, + void (*callback)(int, short, void *), void *arg, + const struct timeval *timeout); + + +/** + 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 *ev, const struct timeval *timeout); + + +/** + 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 *ev, short event, struct timeval *tv); + + +/** + 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 + + +/** + 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); + + +/** + 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 { + u_char *buffer; + u_char *orig_buffer; + + size_t misalign; + size_t totallen; + size_t off; + + void (*cb)(struct evbuffer *, size_t, size_t, void *); + void *cbarg; +}; + +/* Just for error reporting - use other constants otherwise */ +#define EVBUFFER_READ 0x01 +#define EVBUFFER_WRITE 0x02 +#define EVBUFFER_EOF 0x10 +#define EVBUFFER_ERROR 0x20 +#define EVBUFFER_TIMEOUT 0x40 + +struct bufferevent; +typedef void (*evbuffercb)(struct bufferevent *, void *); +typedef void (*everrorcb)(struct bufferevent *, short what, void *); + +struct event_watermark { + size_t low; + size_t high; +}; + +struct bufferevent { + struct event_base *ev_base; + + struct event ev_read; + struct event ev_write; + + struct evbuffer *input; + struct evbuffer *output; + + struct event_watermark wm_read; + struct event_watermark wm_write; + + evbuffercb readcb; + evbuffercb writecb; + everrorcb errorcb; + void *cbarg; + + int timeout_read; /* in seconds */ + int timeout_write; /* in seconds */ + + 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); + + +/** + Changes the callbacks for a bufferevent. + + @param bufev the bufferevent object for which to change callbacks + @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) + @see bufferevent_new() + */ +void bufferevent_setcb(struct bufferevent *bufev, + evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg); + +/** + Changes the file descriptor on which the bufferevent operates. + + @param bufev the bufferevent object for which to change the file descriptor + @param fd the file descriptor to operate on +*/ +void bufferevent_setfd(struct bufferevent *bufev, int fd); + +/** + 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); + + +/** + Sets the watermarks for read and write events. + + On input, a bufferevent does not invoke the user read callback unless + there is at least low watermark data in the buffer. If the read buffer + is beyond the high watermark, the buffevent stops reading from the network. + + On output, the user write callback is invoked whenever the buffered data + falls below the low watermark. + + @param bufev the bufferevent to be modified + @param events EV_READ, EV_WRITE or both + @param lowmark the lower watermark to set + @param highmark the high watermark to set +*/ + +void bufferevent_setwatermark(struct bufferevent *bufev, short events, + size_t lowmark, size_t highmark); + +#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 *); + + +/** + 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 The number of bytes added 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 The number of bytes added 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 + */ +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. + */ + +void evtag_init(void); + +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, ev_uint32_t tag, + struct timeval *tv); + +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, 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, ev_uint32_t need_tag, + struct timeval *ptv); + +#ifdef __cplusplus +} +#endif + +#endif /* _EVENT_H_ */ diff --git a/libevent/event_rpcgen.py b/libevent/event_rpcgen.py new file mode 100644 index 00000000000..5503ff8a5c3 --- /dev/null +++ b/libevent/event_rpcgen.py @@ -0,0 +1,1417 @@ +#!/usr/bin/env python +# +# Copyright (c) 2005 Niels Provos +# All rights reserved. +# +# Generates marshaling code based on libevent. + +import sys +import re + +# +_NAME = "event_rpcgen.py" +_VERSION = "0.1" +_STRUCT_RE = '[a-z][a-z_0-9]*' + +# Globals +line_count = 0 + +white = re.compile(r'^\s+') +cppcomment = re.compile(r'\/\/.*$') +headerdirect = [] +cppdirect = [] + +# Holds everything that makes a struct +class Struct: + def __init__(self, name): + self._name = name + self._entries = [] + self._tags = {} + print >>sys.stderr, ' Created struct: %s' % name + + def AddEntry(self, entry): + if self._tags.has_key(entry.Tag()): + print >>sys.stderr, ( 'Entry "%s" duplicates tag number ' + '%d from "%s" around line %d' ) % ( + entry.Name(), entry.Tag(), + self._tags[entry.Tag()], line_count) + sys.exit(1) + self._entries.append(entry) + self._tags[entry.Tag()] = entry.Name() + print >>sys.stderr, ' Added entry: %s' % entry.Name() + + def Name(self): + return self._name + + def EntryTagName(self, entry): + """Creates the name inside an enumeration for distinguishing data + types.""" + name = "%s_%s" % (self._name, entry.Name()) + return name.upper() + + def PrintIdented(self, file, ident, code): + """Takes an array, add indentation to each entry and prints it.""" + for entry in code: + print >>file, '%s%s' % (ident, entry) + + def PrintTags(self, file): + """Prints the tag definitions for a structure.""" + print >>file, '/* Tag definition for %s */' % self._name + print >>file, 'enum %s_ {' % self._name.lower() + for entry in self._entries: + print >>file, ' %s=%d,' % (self.EntryTagName(entry), + entry.Tag()) + print >>file, ' %s_MAX_TAGS' % (self._name.upper()) + print >>file, '};\n' + + def PrintForwardDeclaration(self, file): + print >>file, 'struct %s;' % self._name + + def PrintDeclaration(self, file): + print >>file, '/* Structure declaration for %s */' % self._name + print >>file, 'struct %s_access_ {' % self._name + for entry in self._entries: + dcl = entry.AssignDeclaration('(*%s_assign)' % entry.Name()) + dcl.extend( + entry.GetDeclaration('(*%s_get)' % entry.Name())) + if entry.Array(): + dcl.extend( + entry.AddDeclaration('(*%s_add)' % entry.Name())) + self.PrintIdented(file, ' ', dcl) + print >>file, '};\n' + + print >>file, 'struct %s {' % self._name + print >>file, ' struct %s_access_ *base;\n' % self._name + for entry in self._entries: + dcl = entry.Declaration() + self.PrintIdented(file, ' ', dcl) + print >>file, '' + for entry in self._entries: + print >>file, ' uint8_t %s_set;' % entry.Name() + print >>file, '};\n' + + print >>file, \ +"""struct %(name)s *%(name)s_new(void); +void %(name)s_free(struct %(name)s *); +void %(name)s_clear(struct %(name)s *); +void %(name)s_marshal(struct evbuffer *, const struct %(name)s *); +int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *); +int %(name)s_complete(struct %(name)s *); +void evtag_marshal_%(name)s(struct evbuffer *, uint32_t, + const struct %(name)s *); +int evtag_unmarshal_%(name)s(struct evbuffer *, uint32_t, + struct %(name)s *);""" % { 'name' : self._name } + + + # Write a setting function of every variable + for entry in self._entries: + self.PrintIdented(file, '', entry.AssignDeclaration( + entry.AssignFuncName())) + self.PrintIdented(file, '', entry.GetDeclaration( + entry.GetFuncName())) + if entry.Array(): + self.PrintIdented(file, '', entry.AddDeclaration( + entry.AddFuncName())) + + print >>file, '/* --- %s done --- */\n' % self._name + + def PrintCode(self, file): + print >>file, ('/*\n' + ' * Implementation of %s\n' + ' */\n') % self._name + + print >>file, \ + 'static struct %(name)s_access_ __%(name)s_base = {' % \ + { 'name' : self._name } + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeBase()) + print >>file, '};\n' + + # Creation + print >>file, ( + 'struct %(name)s *\n' + '%(name)s_new(void)\n' + '{\n' + ' struct %(name)s *tmp;\n' + ' if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {\n' + ' event_warn("%%s: malloc", __func__);\n' + ' return (NULL);\n' + ' }\n' + ' tmp->base = &__%(name)s_base;\n') % { 'name' : self._name } + + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeNew('tmp')) + print >>file, ' tmp->%s_set = 0;\n' % entry.Name() + + print >>file, ( + ' return (tmp);\n' + '}\n') + + # Adding + for entry in self._entries: + if entry.Array(): + self.PrintIdented(file, '', entry.CodeAdd()) + print >>file, '' + + # Assigning + for entry in self._entries: + self.PrintIdented(file, '', entry.CodeAssign()) + print >>file, '' + + # Getting + for entry in self._entries: + self.PrintIdented(file, '', entry.CodeGet()) + print >>file, '' + + # Clearing + print >>file, ( 'void\n' + '%(name)s_clear(struct %(name)s *tmp)\n' + '{' + ) % { 'name' : self._name } + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeClear('tmp')) + + print >>file, '}\n' + + # Freeing + print >>file, ( 'void\n' + '%(name)s_free(struct %(name)s *tmp)\n' + '{' + ) % { 'name' : self._name } + + for entry in self._entries: + self.PrintIdented(file, ' ', entry.CodeFree('tmp')) + + print >>file, (' free(tmp);\n' + '}\n') + + # Marshaling + print >>file, ('void\n' + '%(name)s_marshal(struct evbuffer *evbuf, ' + 'const struct %(name)s *tmp)' + '{') % { 'name' : self._name } + for entry in self._entries: + indent = ' ' + # Optional entries do not have to be set + if entry.Optional(): + indent += ' ' + print >>file, ' if (tmp->%s_set) {' % entry.Name() + self.PrintIdented( + file, indent, + entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp')) + if entry.Optional(): + print >>file, ' }' + + print >>file, '}\n' + + # Unmarshaling + print >>file, ('int\n' + '%(name)s_unmarshal(struct %(name)s *tmp, ' + ' struct evbuffer *evbuf)\n' + '{\n' + ' uint32_t tag;\n' + ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n' + ' if (evtag_peek(evbuf, &tag) == -1)\n' + ' return (-1);\n' + ' switch (tag) {\n' + ) % { 'name' : self._name } + for entry in self._entries: + print >>file, ' case %s:\n' % self.EntryTagName(entry) + if not entry.Array(): + print >>file, ( + ' if (tmp->%s_set)\n' + ' return (-1);' + ) % (entry.Name()) + + self.PrintIdented( + file, ' ', + entry.CodeUnmarshal('evbuf', + self.EntryTagName(entry), 'tmp')) + + print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() + + ' break;\n' ) + print >>file, ( ' default:\n' + ' return -1;\n' + ' }\n' + ' }\n' ) + # Check if it was decoded completely + print >>file, ( ' if (%(name)s_complete(tmp) == -1)\n' + ' return (-1);' + ) % { 'name' : self._name } + + # Successfully decoded + print >>file, ( ' return (0);\n' + '}\n') + + # Checking if a structure has all the required data + print >>file, ( + 'int\n' + '%(name)s_complete(struct %(name)s *msg)\n' + '{' ) % { 'name' : self._name } + for entry in self._entries: + self.PrintIdented( + file, ' ', + entry.CodeComplete('msg')) + print >>file, ( + ' return (0);\n' + '}\n' ) + + # Complete message unmarshaling + print >>file, ( + 'int\n' + 'evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ' + 'uint32_t need_tag, struct %(name)s *msg)\n' + '{\n' + ' uint32_t tag;\n' + ' int res = -1;\n' + '\n' + ' struct evbuffer *tmp = evbuffer_new();\n' + '\n' + ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1' + ' || tag != need_tag)\n' + ' goto error;\n' + '\n' + ' if (%(name)s_unmarshal(msg, tmp) == -1)\n' + ' goto error;\n' + '\n' + ' res = 0;\n' + '\n' + ' error:\n' + ' evbuffer_free(tmp);\n' + ' return (res);\n' + '}\n' ) % { 'name' : self._name } + + # Complete message marshaling + print >>file, ( + 'void\n' + 'evtag_marshal_%(name)s(struct evbuffer *evbuf, uint32_t tag, ' + 'const struct %(name)s *msg)\n' + '{\n' + ' struct evbuffer *_buf = evbuffer_new();\n' + ' assert(_buf != NULL);\n' + ' evbuffer_drain(_buf, -1);\n' + ' %(name)s_marshal(_buf, msg);\n' + ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), ' + 'EVBUFFER_LENGTH(_buf));\n' + ' evbuffer_free(_buf);\n' + '}\n' ) % { 'name' : self._name } + +class Entry: + def __init__(self, type, name, tag): + self._type = type + self._name = name + self._tag = int(tag) + self._ctype = type + self._optional = 0 + self._can_be_array = 0 + self._array = 0 + self._line_count = -1 + self._struct = None + self._refname = None + + def GetTranslation(self): + return { "parent_name" : self._struct.Name(), + "name" : self._name, + "ctype" : self._ctype, + "refname" : self._refname + } + + def SetStruct(self, struct): + self._struct = struct + + def LineCount(self): + assert self._line_count != -1 + return self._line_count + + def SetLineCount(self, number): + self._line_count = number + + def Array(self): + return self._array + + def Optional(self): + return self._optional + + def Tag(self): + return self._tag + + def Name(self): + return self._name + + def Type(self): + return self._type + + def MakeArray(self, yes=1): + self._array = yes + + def MakeOptional(self): + self._optional = 1 + + def GetFuncName(self): + return '%s_%s_get' % (self._struct.Name(), self._name) + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeGet(self): + code = ( + 'int', + '%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, ' + '%(ctype)s *value)', + '{', + ' if (msg->%(name)s_set != 1)', + ' return (-1);', + ' *value = msg->%(name)s_data;', + ' return (0);', + '}' ) + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + + def AssignFuncName(self): + return '%s_%s_assign' % (self._struct.Name(), self._name) + + def AddFuncName(self): + return '%s_%s_add' % (self._struct.Name(), self._name) + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + code = [ 'int', + '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,' + ' const %(ctype)s value)', + '{', + ' msg->%(name)s_set = 1;', + ' msg->%(name)s_data = value;', + ' return (0);', + '}' ] + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()) ] + + return code + + def CodeComplete(self, structname): + if self.Optional(): + return [] + + code = [ 'if (!%s->%s_set)' % (structname, self.Name()), + ' return (-1);' ] + + return code + + def CodeFree(self, name): + return [] + + def CodeBase(self): + code = [ + '%(parent_name)s_%(name)s_assign,', + '%(parent_name)s_%(name)s_get,' + ] + if self.Array(): + code.append('%(parent_name)s_%(name)s_add,') + + code = '\n'.join(code) + code = code % self.GetTranslation() + return code.split('\n') + + def Verify(self): + if self.Array() and not self._can_be_array: + print >>sys.stderr, ( + 'Entry "%s" cannot be created as an array ' + 'around line %d' ) % (self._name, self.LineCount()) + sys.exit(1) + if not self._struct: + print >>sys.stderr, ( + 'Entry "%s" does not know which struct it belongs to ' + 'around line %d' ) % (self._name, self.LineCount()) + sys.exit(1) + if self._optional and self._array: + print >>sys.stderr, ( 'Entry "%s" has illegal combination of ' + 'optional and array around line %d' ) % ( + self._name, self.LineCount() ) + sys.exit(1) + +class EntryBytes(Entry): + def __init__(self, type, name, tag, length): + # Init base class + Entry.__init__(self, type, name, tag) + + self._length = length + self._ctype = 'uint8_t' + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s **);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def Declaration(self): + dcl = ['uint8_t %s_data[%s];' % (self._name, self._length)] + + return dcl + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s **value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, const %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' msg->%s_set = 1;' % name, + ' memcpy(msg->%s_data, value, %s);' % ( + name, self._length), + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = [ 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) + + '%s->%s_data, ' % (var_name, self._name) + + 'sizeof(%s->%s_data)) == -1) {' % ( + var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % ( + buf, tag_name, var_name, self._name, var_name, self._name )] + return code + + def CodeClear(self, structname): + code = [ '%s->%s_set = 0;' % (structname, self.Name()), + 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + structname, self._name, structname, self._name)] + + return code + + def CodeNew(self, name): + code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( + name, self._name, name, self._name)] + return code + + def Verify(self): + if not self._length: + print >>sys.stderr, 'Entry "%s" needs a length around line %d' % ( + self._name, self.LineCount() ) + sys.exit(1) + + Entry.Verify(self) + +class EntryInt(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'uint32_t' + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % ( + buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % ( + buf, tag_name, var_name, self._name)] + return code + + def Declaration(self): + dcl = ['uint32_t %s_data;' % self._name] + + return dcl + + def CodeNew(self, name): + code = ['%s->%s_data = 0;' % (name, self._name)] + return code + +class EntryString(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'char *' + + def CodeAssign(self): + name = self._name + code = """int +%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, + const %(ctype)s value) +{ + if (msg->%(name)s_data != NULL) + free(msg->%(name)s_data); + if ((msg->%(name)s_data = strdup(value)) == NULL) + return (-1); + msg->%(name)s_set = 1; + return (0); +}""" % self.GetTranslation() + + return code.split('\n') + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % ( + buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % ( + buf, tag_name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free (%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free (%s->%s_data); ' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['char *%s_data;' % self._name] + + return dcl + +class EntryStruct(Entry): + def __init__(self, type, name, tag, refname): + # Init base class + Entry.__init__(self, type, name, tag) + + self._can_be_array = 1 + self._refname = refname + self._ctype = 'struct %s*' % refname + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1) {' % name, + ' msg->%s_data = %s_new();' % (name, self._refname), + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' }', + ' *value = msg->%s_data;' % name, + ' return (0);', + '}' ] + return code + + def CodeAssign(self): + name = self._name + code = """int +%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, + const %(ctype)s value) +{ + struct evbuffer *tmp = NULL; + if (msg->%(name)s_set) { + %(refname)s_clear(msg->%(name)s_data); + msg->%(name)s_set = 0; + } else { + msg->%(name)s_data = %(refname)s_new(); + if (msg->%(name)s_data == NULL) { + event_warn("%%s: %(refname)s_new()", __func__); + goto error; + } + } + if ((tmp = evbuffer_new()) == NULL) { + event_warn("%%s: evbuffer_new()", __func__); + goto error; + } + %(refname)s_marshal(tmp, value); + if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) { + event_warnx("%%s: %(refname)s_unmarshal", __func__); + goto error; + } + msg->%(name)s_set = 1; + evbuffer_free(tmp); + return (0); + error: + if (tmp != NULL) + evbuffer_free(tmp); + if (msg->%(name)s_data != NULL) { + %(refname)s_free(msg->%(name)s_data); + msg->%(name)s_data = NULL; + } + return (-1); +}""" % self.GetTranslation() + return code.split('\n') + + def CodeComplete(self, structname): + if self.Optional(): + code = [ 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % ( + structname, self.Name(), + self._refname, structname, self.Name()), + ' return (-1);' ] + else: + code = [ 'if (%s_complete(%s->%s_data) == -1)' % ( + self._refname, structname, self.Name()), + ' return (-1);' ] + + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['%s->%s_data = %s_new();' % ( + var_name, self._name, self._refname), + 'if (%s->%s_data == NULL)' % (var_name, self._name), + ' return (-1);', + 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % ( + self._refname, buf, tag_name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal_%s(%s, %s, %s->%s_data);' % ( + self._refname, buf, tag_name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' %s_free(%s->%s_data);' % ( + self._refname, structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name)] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' %s_free(%s->%s_data); ' % ( + self._refname, name, self._name)] + + return code + + def Declaration(self): + dcl = ['%s %s_data;' % (self._ctype, self._name)] + + return dcl + +class EntryVarBytes(Entry): + def __init__(self, type, name, tag): + # Init base class + Entry.__init__(self, type, name, tag) + + self._ctype = 'uint8_t *' + + def GetDeclaration(self, funcname): + code = [ 'int %s(struct %s *, %s *, uint32_t *);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, const %s, uint32_t);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def CodeAssign(self): + name = self._name + code = [ 'int', + '%s_%s_assign(struct %s *msg, ' + 'const %s value, uint32_t len)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_data != NULL)' % name, + ' free (msg->%s_data);' % name, + ' msg->%s_data = malloc(len);' % name, + ' if (msg->%s_data == NULL)' % name, + ' return (-1);', + ' msg->%s_set = 1;' % name, + ' msg->%s_length = len;' % name, + ' memcpy(msg->%s_data, value, len);' % name, + ' return (0);', + '}' ] + return code + + def CodeGet(self): + name = self._name + code = [ 'int', + '%s_%s_get(struct %s *msg, %s *value, uint32_t *plen)' % ( + self._struct.Name(), name, + self._struct.Name(), self._ctype), + '{', + ' if (msg->%s_set != 1)' % name, + ' return (-1);', + ' *value = msg->%s_data;' % name, + ' *plen = msg->%s_length;' % name, + ' return (0);', + '}' ] + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + code = ['if (evtag_payload_length(%s, &%s->%s_length) == -1)' % ( + buf, var_name, self._name), + ' return (-1);', + # We do not want DoS opportunities + 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % ( + var_name, self._name, buf), + ' return (-1);', + 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % ( + var_name, self._name, var_name, self._name), + ' return (-1);', + 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, ' + '%s->%s_length) == -1) {' % ( + buf, tag_name, var_name, self._name, var_name, self._name), + ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( + self._name ), + ' return (-1);', + '}' + ] + return code + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % ( + buf, tag_name, var_name, self._name, var_name, self._name)] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' free (%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_length = 0;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name) ] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL)' % (name, self._name), + ' free (%s->%s_data); ' % (name, self._name)] + + return code + + def Declaration(self): + dcl = ['uint8_t *%s_data;' % self._name, + 'uint32_t %s_length;' % self._name] + + return dcl + +class EntryArray(Entry): + def __init__(self, entry): + # Init base class + Entry.__init__(self, entry._type, entry._name, entry._tag) + + self._entry = entry + self._refname = entry._refname + self._ctype = 'struct %s *' % self._refname + + def GetDeclaration(self, funcname): + """Allows direct access to elements of the array.""" + translate = self.GetTranslation() + translate["funcname"] = funcname + code = [ + 'int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);' % + translate ] + return code + + def AssignDeclaration(self, funcname): + code = [ 'int %s(struct %s *, int, const %s);' % ( + funcname, self._struct.Name(), self._ctype ) ] + return code + + def AddDeclaration(self, funcname): + code = [ '%s %s(struct %s *);' % ( + self._ctype, funcname, self._struct.Name() ) ] + return code + + def CodeGet(self): + code = """int +%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset, + %(ctype)s *value) +{ + if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length) + return (-1); + *value = msg->%(name)s_data[offset]; + return (0); +}""" % self.GetTranslation() + + return code.split('\n') + + def CodeAssign(self): + code = """int +%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off, + const %(ctype)s value) +{ + struct evbuffer *tmp = NULL; + if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length) + return (-1); + %(refname)s_clear(msg->%(name)s_data[off]); + if ((tmp = evbuffer_new()) == NULL) { + event_warn("%%s: evbuffer_new()", __func__); + goto error; + } + %(refname)s_marshal(tmp, value); + if (%(refname)s_unmarshal(msg->%(name)s_data[off], tmp) == -1) { + event_warnx("%%s: %(refname)s_unmarshal", __func__); + goto error; + } + evbuffer_free(tmp); + return (0); +error: + if (tmp != NULL) + evbuffer_free(tmp); + %(refname)s_clear(msg->%(name)s_data[off]); + return (-1); +}""" % self.GetTranslation() + + return code.split('\n') + + def CodeAdd(self): + code = \ +"""%(ctype)s +%(parent_name)s_%(name)s_add(struct %(parent_name)s *msg) +{ + if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) { + int tobe_allocated = msg->%(name)s_num_allocated; + %(ctype)s* new_data = NULL; + tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1; + new_data = (%(ctype)s*) realloc(msg->%(name)s_data, + tobe_allocated * sizeof(%(ctype)s)); + if (new_data == NULL) + goto error; + msg->%(name)s_data = new_data; + msg->%(name)s_num_allocated = tobe_allocated; + } + msg->%(name)s_data[msg->%(name)s_length - 1] = %(refname)s_new(); + if (msg->%(name)s_data[msg->%(name)s_length - 1] == NULL) + goto error; + msg->%(name)s_set = 1; + return (msg->%(name)s_data[msg->%(name)s_length - 1]); +error: + --msg->%(name)s_length; + return (NULL); +} + """ % self.GetTranslation() + + return code.split('\n') + + def CodeComplete(self, structname): + code = [] + translate = self.GetTranslation() + + if self.Optional(): + code.append( 'if (%(structname)s->%(name)s_set)' % translate) + + translate["structname"] = structname + tmp = """{ + int i; + for (i = 0; i < %(structname)s->%(name)s_length; ++i) { + if (%(refname)s_complete(%(structname)s->%(name)s_data[i]) == -1) + return (-1); + } +}""" % translate + code.extend(tmp.split('\n')) + + return code + + def CodeUnmarshal(self, buf, tag_name, var_name): + translate = self.GetTranslation() + translate["var_name"] = var_name + translate["buf"] = buf + translate["tag_name"] = tag_name + code = """if (%(parent_name)s_%(name)s_add(%(var_name)s) == NULL) + return (-1); +if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag_name)s, + %(var_name)s->%(name)s_data[%(var_name)s->%(name)s_length - 1]) == -1) { + --%(var_name)s->%(name)s_length; + event_warnx("%%s: failed to unmarshal %(name)s", __func__); + return (-1); +}""" % translate + + return code.split('\n') + + def CodeMarshal(self, buf, tag_name, var_name): + code = ['{', + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + var_name, self._name), + ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % ( + self._refname, buf, tag_name, var_name, self._name), + ' }', + '}' + ] + return code + + def CodeClear(self, structname): + code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + structname, self.Name()), + ' %s_free(%s->%s_data[i]);' % ( + self._refname, structname, self.Name()), + ' }', + ' free(%s->%s_data);' % (structname, self.Name()), + ' %s->%s_data = NULL;' % (structname, self.Name()), + ' %s->%s_set = 0;' % (structname, self.Name()), + ' %s->%s_length = 0;' % (structname, self.Name()), + ' %s->%s_num_allocated = 0;' % (structname, self.Name()), + '}' + ] + + return code + + def CodeNew(self, name): + code = ['%s->%s_data = NULL;' % (name, self._name), + '%s->%s_length = 0;' % (name, self._name), + '%s->%s_num_allocated = 0;' % (name, self._name)] + return code + + def CodeFree(self, name): + code = ['if (%s->%s_data != NULL) {' % (name, self._name), + ' int i;', + ' for (i = 0; i < %s->%s_length; ++i) {' % ( + name, self._name), + ' %s_free(%s->%s_data[i]); ' % ( + self._refname, name, self._name), + ' %s->%s_data[i] = NULL;' % (name, self._name), + ' }', + ' free(%s->%s_data);' % (name, self._name), + ' %s->%s_data = NULL;' % (name, self._name), + ' %s->%s_length = 0;' % (name, self._name), + ' %s->%s_num_allocated = 0;' % (name, self._name), + '}' + ] + + return code + + def Declaration(self): + dcl = ['struct %s **%s_data;' % (self._refname, self._name), + 'int %s_length;' % self._name, + 'int %s_num_allocated;' % self._name ] + + return dcl + +def NormalizeLine(line): + global white + global cppcomment + + line = cppcomment.sub('', line) + line = line.strip() + line = white.sub(' ', line) + + return line + +def ProcessOneEntry(newstruct, entry): + optional = 0 + array = 0 + entry_type = '' + name = '' + tag = '' + tag_set = None + separator = '' + fixed_length = '' + + tokens = entry.split(' ') + while tokens: + token = tokens[0] + tokens = tokens[1:] + + if not entry_type: + if not optional and token == 'optional': + optional = 1 + continue + + if not array and token == 'array': + array = 1 + continue + + if not entry_type: + entry_type = token + continue + + if not name: + res = re.match(r'^([^\[\]]+)(\[.*\])?$', token) + if not res: + print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % ( + entry, line_count) + sys.exit(1) + name = res.group(1) + fixed_length = res.group(2) + if fixed_length: + fixed_length = fixed_length[1:-1] + continue + + if not separator: + separator = token + if separator != '=': + print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % ( + name, token) + sys.exit(1) + continue + + if not tag_set: + tag_set = 1 + if not re.match(r'^(0x)?[0-9]+$', token): + print >>sys.stderr, 'Expected tag number: \"%s\"' % entry + sys.exit(1) + tag = int(token, 0) + continue + + print >>sys.stderr, 'Cannot parse \"%s\"' % entry + sys.exit(1) + + if not tag_set: + print >>sys.stderr, 'Need tag number: \"%s\"' % entry + sys.exit(1) + + # Create the right entry + if entry_type == 'bytes': + if fixed_length: + newentry = EntryBytes(entry_type, name, tag, fixed_length) + else: + newentry = EntryVarBytes(entry_type, name, tag) + elif entry_type == 'int' and not fixed_length: + newentry = EntryInt(entry_type, name, tag) + elif entry_type == 'string' and not fixed_length: + newentry = EntryString(entry_type, name, tag) + else: + res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE, + entry_type, re.IGNORECASE) + if res: + # References another struct defined in our file + newentry = EntryStruct(entry_type, name, tag, res.group(1)) + else: + print >>sys.stderr, 'Bad type: "%s" in "%s"' % (entry_type, entry) + sys.exit(1) + + structs = [] + + if optional: + newentry.MakeOptional() + if array: + newentry.MakeArray() + + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.Verify() + + if array: + # We need to encapsulate this entry into a struct + newname = newentry.Name()+ '_array' + + # Now borgify the new entry. + newentry = EntryArray(newentry) + newentry.SetStruct(newstruct) + newentry.SetLineCount(line_count) + newentry.MakeArray() + + newstruct.AddEntry(newentry) + + return structs + +def ProcessStruct(data): + tokens = data.split(' ') + + # First three tokens are: 'struct' 'name' '{' + newstruct = Struct(tokens[1]) + + inside = ' '.join(tokens[3:-1]) + + tokens = inside.split(';') + + structs = [] + + for entry in tokens: + entry = NormalizeLine(entry) + if not entry: + continue + + # It's possible that new structs get defined in here + structs.extend(ProcessOneEntry(newstruct, entry)) + + structs.append(newstruct) + return structs + +def GetNextStruct(file): + global line_count + global cppdirect + + got_struct = 0 + + processed_lines = [] + + have_c_comment = 0 + data = '' + while 1: + line = file.readline() + if not line: + break + + line_count += 1 + line = line[:-1] + + if not have_c_comment and re.search(r'/\*', line): + if re.search(r'/\*.*\*/', line): + line = re.sub(r'/\*.*\*/', '', line) + else: + line = re.sub(r'/\*.*$', '', line) + have_c_comment = 1 + + if have_c_comment: + if not re.search(r'\*/', line): + continue + have_c_comment = 0 + line = re.sub(r'^.*\*/', '', line) + + line = NormalizeLine(line) + + if not line: + continue + + if not got_struct: + if re.match(r'#include ["<].*[>"]', line): + cppdirect.append(line) + continue + + if re.match(r'^#(if( |def)|endif)', line): + cppdirect.append(line) + continue + + if re.match(r'^#define', line): + headerdirect.append(line) + continue + + if not re.match(r'^struct %s {$' % _STRUCT_RE, + line, re.IGNORECASE): + print >>sys.stderr, 'Missing struct on line %d: %s' % ( + line_count, line) + sys.exit(1) + else: + got_struct = 1 + data += line + continue + + # We are inside the struct + tokens = line.split('}') + if len(tokens) == 1: + data += ' ' + line + continue + + if len(tokens[1]): + print >>sys.stderr, 'Trailing garbage after struct on line %d' % ( + line_count ) + sys.exit(1) + + # We found the end of the struct + data += ' %s}' % tokens[0] + break + + # Remove any comments, that might be in there + data = re.sub(r'/\*.*\*/', '', data) + + return data + + +def Parse(file): + """ + Parses the input file and returns C code and corresponding header file. + """ + + entities = [] + + while 1: + # Just gets the whole struct nicely formatted + data = GetNextStruct(file) + + if not data: + break + + entities.extend(ProcessStruct(data)) + + return entities + +def GuardName(name): + name = '_'.join(name.split('.')) + name = '_'.join(name.split('/')) + guard = '_'+name.upper()+'_' + + return guard + +def HeaderPreamble(name): + guard = GuardName(name) + pre = ( + '/*\n' + ' * Automatically generated from %s\n' + ' */\n\n' + '#ifndef %s\n' + '#define %s\n\n' ) % ( + name, guard, guard) + + # insert stdint.h - let's hope everyone has it + pre += ( + '#include \n' + '#ifdef _EVENT_HAVE_STDINT_H\n' + '#include \n' + '#endif\n' ) + + for statement in headerdirect: + pre += '%s\n' % statement + if headerdirect: + pre += '\n' + + pre += ( + '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n' + '#ifdef __GNUC__\n' + '#define EVTAG_ASSIGN(msg, member, args...) ' + '(*(msg)->base->member##_assign)(msg, ## args)\n' + '#define EVTAG_GET(msg, member, args...) ' + '(*(msg)->base->member##_get)(msg, ## args)\n' + '#else\n' + '#define EVTAG_ASSIGN(msg, member, ...) ' + '(*(msg)->base->member##_assign)(msg, ## __VA_ARGS__)\n' + '#define EVTAG_GET(msg, member, ...) ' + '(*(msg)->base->member##_get)(msg, ## __VA_ARGS__)\n' + '#endif\n' + '#define EVTAG_ADD(msg, member) (*(msg)->base->member##_add)(msg)\n' + '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n' + ) + + return pre + + +def HeaderPostamble(name): + guard = GuardName(name) + return '#endif /* %s */' % guard + +def BodyPreamble(name): + global _NAME + global _VERSION + + header_file = '.'.join(name.split('.')[:-1]) + '.gen.h' + + pre = ( '/*\n' + ' * Automatically generated from %s\n' + ' * by %s/%s. DO NOT EDIT THIS FILE.\n' + ' */\n\n' ) % (name, _NAME, _VERSION) + pre += ( '#include \n' + '#include \n' + '#include \n' + '#include \n' + '#include \n' + '#include \n\n' ) + + for statement in cppdirect: + pre += '%s\n' % statement + + pre += '\n#include "%s"\n\n' % header_file + + pre += 'void event_err(int eval, const char *fmt, ...);\n' + pre += 'void event_warn(const char *fmt, ...);\n' + pre += 'void event_errx(int eval, const char *fmt, ...);\n' + pre += 'void event_warnx(const char *fmt, ...);\n\n' + + return pre + +def main(argv): + if len(argv) < 2 or not argv[1]: + print >>sys.stderr, 'Need RPC description file as first argument.' + sys.exit(1) + + filename = argv[1] + + ext = filename.split('.')[-1] + if ext != 'rpc': + print >>sys.stderr, 'Unrecognized file extension: %s' % ext + sys.exit(1) + + print >>sys.stderr, 'Reading \"%s\"' % filename + + fp = open(filename, 'r') + entities = Parse(fp) + fp.close() + + header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h' + impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c' + + print >>sys.stderr, '... creating "%s"' % header_file + header_fp = open(header_file, 'w') + print >>header_fp, HeaderPreamble(filename) + + # Create forward declarations: allows other structs to reference + # each other + for entry in entities: + entry.PrintForwardDeclaration(header_fp) + print >>header_fp, '' + + for entry in entities: + entry.PrintTags(header_fp) + entry.PrintDeclaration(header_fp) + print >>header_fp, HeaderPostamble(filename) + header_fp.close() + + print >>sys.stderr, '... creating "%s"' % impl_file + impl_fp = open(impl_file, 'w') + print >>impl_fp, BodyPreamble(filename) + for entry in entities: + entry.PrintCode(impl_fp) + impl_fp.close() + +if __name__ == '__main__': + main(sys.argv) diff --git a/libevent/event_tagging.c b/libevent/event_tagging.c new file mode 100644 index 00000000000..d436e3fd65b --- /dev/null +++ b/libevent/event_tagging.c @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2003, 2004 Niels Provos + * 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 HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#undef WIN32_LEAN_AND_MEAN +#else +#include +#endif + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "event.h" +#include "evutil.h" +#include "log.h" + +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(void) +{ + if (_buf != NULL) + return; + + if ((_buf = evbuffer_new()) == NULL) + event_err(1, "%s: malloc", __func__); +} + +/* + * 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. + */ + +void +encode_int(struct evbuffer *evbuf, ev_uint32_t number) +{ + int off = 1, nibbles = 0; + ev_uint8_t data[5]; + + memset(data, 0, sizeof(ev_uint32_t)+1); + while (number) { + if (off & 0x1) + data[off/2] = (data[off/2] & 0xf0) | (number & 0x0f); + else + data[off/2] = (data[off/2] & 0x0f) | + ((number & 0x0f) << 4); + number >>= 4; + off++; + } + + if (off > 2) + nibbles = off - 2; + + /* Off - 1 is the number of encoded nibbles */ + data[0] = (data[0] & 0x0f) | ((nibbles & 0x0f) << 4); + + evbuffer_add(evbuf, data, (off + 1) / 2); +} + +/* + * 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, ev_uint32_t tag, + const void *data, ev_uint32_t len) +{ + 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, ev_uint32_t tag, ev_uint32_t integer) +{ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + encode_int(_buf, integer); + + evtag_encode_tag(evbuf, tag); + encode_int(evbuf, EVBUFFER_LENGTH(_buf)); + evbuffer_add_buffer(evbuf, _buf); +} + +void +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, ev_uint32_t tag, struct timeval *tv) +{ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + encode_int(_buf, tv->tv_sec); + encode_int(_buf, tv->tv_usec); + + evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), + EVBUFFER_LENGTH(_buf)); +} + +static int +decode_int_internal(ev_uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) +{ + ev_uint32_t number = 0; + ev_uint8_t *data = EVBUFFER_DATA(evbuf); + int len = EVBUFFER_LENGTH(evbuf); + int nibbles = 0; + + if (!len) + return (-1); + + nibbles = ((data[0] & 0xf0) >> 4) + 1; + if (nibbles > 8 || (nibbles >> 1) + 1 > len) + return (-1); + len = (nibbles >> 1) + 1; + + while (nibbles > 0) { + number <<= 4; + if (nibbles & 0x1) + number |= data[nibbles >> 1] & 0x0f; + else + number |= (data[nibbles >> 1] & 0xf0) >> 4; + nibbles--; + } + + if (dodrain) + evbuffer_drain(evbuf, len); + + *pnumber = number; + + return (len); +} + +int +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, ev_uint32_t *ptag) +{ + return (decode_tag_internal(ptag, evbuf, 0 /* dodrain */)); +} + +int +evtag_peek_length(struct evbuffer *evbuf, ev_uint32_t *plength) +{ + struct evbuffer tmp; + int res, len; + + len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); + if (len == -1) + return (-1); + + tmp = *evbuf; + tmp.buffer += len; + tmp.off -= len; + + res = decode_int_internal(plength, &tmp, 0); + if (res == -1) + return (-1); + + *plength += res + len; + + return (0); +} + +int +evtag_payload_length(struct evbuffer *evbuf, ev_uint32_t *plength) +{ + struct evbuffer tmp; + int res, len; + + len = decode_tag_internal(NULL, evbuf, 0 /* dodrain */); + if (len == -1) + return (-1); + + tmp = *evbuf; + tmp.buffer += len; + tmp.off -= len; + + res = decode_int_internal(plength, &tmp, 0); + if (res == -1) + return (-1); + + return (0); +} + +int +evtag_consume(struct evbuffer *evbuf) +{ + 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); + + return (0); +} + +/* Reads the data type from an event buffer */ + +int +evtag_unmarshal(struct evbuffer *src, ev_uint32_t *ptag, struct evbuffer *dst) +{ + ev_uint32_t len; + ev_uint32_t integer; + + if (decode_tag_internal(ptag, src, 1 /* dodrain */) == -1) + return (-1); + if (evtag_decode_int(&integer, src) == -1) + return (-1); + len = integer; + + if (EVBUFFER_LENGTH(src) < len) + return (-1); + + if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) + return (-1); + + evbuffer_drain(src, len); + + return (len); +} + +/* Marshaling for integers */ + +int +evtag_unmarshal_int(struct evbuffer *evbuf, ev_uint32_t need_tag, + ev_uint32_t *pinteger) +{ + ev_uint32_t tag; + ev_uint32_t len; + ev_uint32_t integer; + + 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; + + if (EVBUFFER_LENGTH(evbuf) < len) + return (-1); + + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1) + return (-1); + + evbuffer_drain(evbuf, len); + + return (evtag_decode_int(pinteger, _buf)); +} + +/* Unmarshal a fixed length tag */ + +int +evtag_unmarshal_fixed(struct evbuffer *src, ev_uint32_t need_tag, void *data, + size_t len) +{ + ev_uint32_t tag; + + /* Initialize this event buffer so that we can read into it */ + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + /* Now unmarshal a tag and check that it matches the tag we want */ + if (evtag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag) + return (-1); + + if (EVBUFFER_LENGTH(_buf) != len) + return (-1); + + memcpy(data, EVBUFFER_DATA(_buf), len); + return (0); +} + +int +evtag_unmarshal_string(struct evbuffer *evbuf, ev_uint32_t need_tag, + char **pstring) +{ + ev_uint32_t tag; + + evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); + + if (evtag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) + return (-1); + + *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1); + if (*pstring == NULL) + event_err(1, "%s: calloc", __func__); + evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf)); + + return (0); +} + +int +evtag_unmarshal_timeval(struct evbuffer *evbuf, ev_uint32_t need_tag, + struct timeval *ptv) +{ + 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 (evtag_decode_int(&integer, _buf) == -1) + return (-1); + ptv->tv_sec = integer; + if (evtag_decode_int(&integer, _buf) == -1) + return (-1); + ptv->tv_usec = integer; + + return (0); +} diff --git a/libevent/evhttp.h b/libevent/evhttp.h new file mode 100644 index 00000000000..99d16a2f47a --- /dev/null +++ b/libevent/evhttp.h @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 _EVHTTP_H_ +#define _EVHTTP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#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 + * interesting applications are networked today, I have often found the + * need to write HTTP code. The following prototypes and definitions provide + * an application with a minimal interface for making HTTP requests and for + * creating a very simple HTTP server. + */ + +/* Response codes */ +#define HTTP_OK 200 +#define HTTP_NOCONTENT 204 +#define HTTP_MOVEPERM 301 +#define HTTP_MOVETEMP 302 +#define HTTP_NOTMODIFIED 304 +#define HTTP_BADREQUEST 400 +#define HTTP_NOTFOUND 404 +#define HTTP_SERVUNAVAIL 503 + +struct evhttp; +struct evhttp_request; +struct evkeyvalq; + +/** 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); + +/** + * Makes an HTTP server accept connections on the specified socket + * + * This may be useful to create a socket and then fork multiple instances + * of an http server, or when a socket has been communicated via file + * descriptor passing in situations where an http servers does not have + * permissions to bind to a low-numbered port. + * + * Can be called multiple times to have the http server listen to + * multiple different sockets. + * + * @param http a pointer to an evhttp object + * @param fd a socket fd that is ready for accepting connections + * @return 0 on success, -1 on failure. + * @see evhttp_free(), evhttp_bind_socket() + */ +int evhttp_accept_socket(struct evhttp *http, int fd); + +/** + * 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 */ +void evhttp_set_cb(struct evhttp *, const char *, + void (*)(struct evhttp_request *, void *), void *); + +/** 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 + */ +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 */ + +/** + * 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 *); + +/** + * 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_PROXY_REQUEST 0x0002 + + struct evkeyvalq *input_headers; + struct evkeyvalq *output_headers; + + /* address of the remote host and the port connection came from */ + char *remote_host; + u_short remote_port; + + enum evhttp_request_kind kind; + enum evhttp_cmd_type type; + + char *uri; /* uri after HTTP request was parsed */ + + char major; /* HTTP Major number */ + char minor; /* HTTP Minor number */ + + int response_code; /* HTTP Response code */ + char *response_code_line; /* Readable response */ + + struct evbuffer *input_buffer; /* read data */ + ev_int64_t ntoread; + int chunked; + + struct evbuffer *output_buffer; /* outgoing post or data */ + + /* Callback */ + 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. + */ + 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. + */ +struct evhttp_request *evhttp_request_new( + void (*cb)(struct evhttp_request *, void *), void *arg); + +/** 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. */ +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. + */ +struct evhttp_connection *evhttp_connection_new( + const char *address, unsigned short port); + +/** Frees an http connection */ +void evhttp_connection_free(struct evhttp_connection *evcon); + +/** 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 local port from which http connections are made */ +void evhttp_connection_set_local_port(struct evhttp_connection *evcon, + unsigned short port); + +/** 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 */ +void evhttp_connection_set_retries(struct evhttp_connection *evcon, + int retry_max); + +/** Set a callback for connection close. */ +void evhttp_connection_set_closecb(struct evhttp_connection *evcon, + void (*)(struct evhttp_connection *, void *), void *); + +/** + * 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 */ +int evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri); + +const char *evhttp_request_uri(struct evhttp_request *req); + +/* Interfaces for dealing with HTTP headers */ + +const char *evhttp_find_header(const struct evkeyvalq *, const char *); +int evhttp_remove_header(struct evkeyvalq *, const char *); +int evhttp_add_header(struct evkeyvalq *, const char *, const char *); +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. + * + * Parsing a uri like + * + * http://foo.com/?q=test&s=some+thing + * + * will result in two entries in the key value queue. + + * The first entry is: key="q", value="test" + * The second entry is: key="s", value="some thing" + * + * @param uri the request URI + * @param headers the head of the evkeyval queue + */ +void evhttp_parse_query(const char *uri, struct evkeyvalq *headers); + + +/** + * Escape HTML character entities in a string. + * + * Replaces <, >, ", ' and & with <, >, ", + * ' and & 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 + +#endif /* _EVHTTP_H_ */ diff --git a/libevent/evport.c b/libevent/evport.c new file mode 100644 index 00000000000..dae6900cc10 --- /dev/null +++ b/libevent/evport.c @@ -0,0 +1,513 @@ +/* + * Submitted by David Pacheco (dp.spambait@gmail.com) + * + * 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 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) 2007 Sun Microsystems. All rights reserved. + * Use is subject to license terms. + */ + +/* + * evport.c: event backend using Solaris 10 event ports. See port_create(3C). + * This implementation is loosely modeled after the one used for select(2) (in + * select.c). + * + * The outstanding events are tracked in a data structure called evport_data. + * Each entry in the ed_fds array corresponds to a file descriptor, and contains + * pointers to the read and write events that correspond to that fd. (That is, + * when the file is readable, the "read" event should handle it, etc.) + * + * evport_add and evport_del update this data structure. evport_dispatch uses it + * to determine where to callback when an event occurs (which it gets from + * port_getn). + * + * Helper functions are used: grow() grows the file descriptor array as + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CHECK_INVARIANTS +#include +#endif + +#include "event.h" +#include "event-internal.h" +#include "log.h" +#include "evsignal.h" + + +/* + * Default value for ed_nevents, which is the maximum file descriptor number we + * can handle. If an event comes in for a file descriptor F > nevents, we will + * grow the array of file descriptors, doubling its size. + */ +#define DEFAULT_NFDS 16 + + +/* + * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on + * any particular call. You can speed things up by increasing this, but it will + * (obviously) require more memory. + */ +#define EVENTS_PER_GETN 8 + +/* + * Per-file-descriptor information about what events we're subscribed to. These + * fields are NULL if no event is subscribed to either of them. + */ + +struct fd_info { + struct event* fdi_revt; /* the event responsible for the "read" */ + struct event* fdi_wevt; /* the event responsible for the "write" */ +}; + +#define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL) +#define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL) +#define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi)) +#define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \ + (FDI_HAS_WRITE(fdi) ? POLLOUT : 0) + +struct evport_data { + int ed_port; /* event port for system events */ + int ed_nevents; /* number of allocated fdi's */ + struct fd_info *ed_fds; /* allocated fdi table */ + /* fdi's that we need to reassoc */ + 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_dispatch (struct event_base *, void *, struct timeval *); +static void evport_dealloc (struct event_base *, void *); + +const struct eventop evportops = { + "evport", + evport_init, + evport_add, + evport_del, + evport_dispatch, + evport_dealloc, + 1 /* need reinit */ +}; + +/* + * Initialize the event port implementation. + */ + +static void* +evport_init(struct event_base *base) +{ + struct evport_data *evpd; + int i; + /* + * Disable event ports when this environment variable is set + */ + if (getenv("EVENT_NOEVPORT")) + return (NULL); + + if (!(evpd = calloc(1, sizeof(struct evport_data)))) + return (NULL); + + if ((evpd->ed_port = port_create()) == -1) { + free(evpd); + return (NULL); + } + + /* + * Initialize file descriptor structure + */ + evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info)); + if (evpd->ed_fds == NULL) { + close(evpd->ed_port); + free(evpd); + return (NULL); + } + evpd->ed_nevents = DEFAULT_NFDS; + for (i = 0; i < EVENTS_PER_GETN; i++) + evpd->ed_pending[i] = -1; + + evsignal_init(base); + + return (evpd); +} + +#ifdef CHECK_INVARIANTS +/* + * Checks some basic properties about the evport_data structure. Because it + * checks all file descriptors, this function can be expensive when the maximum + * file descriptor ever used is rather large. + */ + +static void +check_evportop(struct evport_data *evpd) +{ + assert(evpd); + assert(evpd->ed_nevents > 0); + assert(evpd->ed_port > 0); + assert(evpd->ed_fds > 0); + + /* + * Verify the integrity of the fd_info struct as well as the events to + * which it points (at least, that they're valid references and correct + * for their position in the structure). + */ + int i; + for (i = 0; i < evpd->ed_nevents; ++i) { + struct event *ev; + struct fd_info *fdi; + + fdi = &evpd->ed_fds[i]; + if ((ev = fdi->fdi_revt) != NULL) { + assert(ev->ev_fd == i); + } + if ((ev = fdi->fdi_wevt) != NULL) { + assert(ev->ev_fd == i); + } + } +} + +/* + * Verifies very basic integrity of a given port_event. + */ +static void +check_event(port_event_t* pevt) +{ + /* + * We've only registered for PORT_SOURCE_FD events. The only + * other thing we can legitimately receive is PORT_SOURCE_ALERT, + * but since we're not using port_alert either, we can assume + * PORT_SOURCE_FD. + */ + assert(pevt->portev_source == PORT_SOURCE_FD); + assert(pevt->portev_user == NULL); +} + +#else +#define check_evportop(epop) +#define check_event(pevt) +#endif /* CHECK_INVARIANTS */ + +/* + * Doubles the size of the allocated file descriptor array. + */ +static int +grow(struct evport_data *epdp, int factor) +{ + struct fd_info *tmp; + int oldsize = epdp->ed_nevents; + int newsize = factor * oldsize; + assert(factor > 1); + + check_evportop(epdp); + + tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize); + if (NULL == tmp) + return -1; + epdp->ed_fds = tmp; + memset((char*) (epdp->ed_fds + oldsize), 0, + (newsize - oldsize)*sizeof(struct fd_info)); + epdp->ed_nevents = newsize; + + check_evportop(epdp); + + return 0; +} + + +/* + * (Re)associates the given file descriptor with the event port. The OS events + * are specified (implicitly) from the fd_info struct. + */ +static int +reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd) +{ + int sysevents = FDI_TO_SYSEVENTS(fdip); + + if (sysevents != 0) { + if (port_associate(epdp->ed_port, PORT_SOURCE_FD, + fd, sysevents, NULL) == -1) { + event_warn("port_associate"); + return (-1); + } + } + + check_evportop(epdp); + + return (0); +} + +/* + * Main event loop - polls port_getn for some number of events, and processes + * them. + */ + +static int +evport_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + int i, res; + struct evport_data *epdp = arg; + port_event_t pevtlist[EVENTS_PER_GETN]; + + /* + * port_getn will block until it has at least nevents events. It will + * also return how many it's given us (which may be more than we asked + * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in + * nevents. + */ + int nevents = 1; + + /* + * We have to convert a struct timeval to a struct timespec + * (only difference is nanoseconds vs. microseconds). If no time-based + * events are active, we should wait for I/O (and tv == NULL). + */ + struct timespec ts; + struct timespec *ts_p = NULL; + if (tv != NULL) { + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; + ts_p = &ts; + } + + /* + * Before doing anything else, we need to reassociate the events we hit + * last time which need reassociation. See comment at the end of the + * loop below. + */ + for (i = 0; i < EVENTS_PER_GETN; ++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] = -1; + } + } + + if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, + (unsigned int *) &nevents, ts_p)) == -1) { + if (errno == EINTR || errno == EAGAIN) { + evsignal_process(base); + return (0); + } else if (errno == ETIME) { + if (nevents == 0) + return (0); + } else { + event_warn("port_getn"); + return (-1); + } + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + event_debug(("%s: port_getn reports %d events", __func__, nevents)); + + for (i = 0; i < nevents; ++i) { + struct event *ev; + struct fd_info *fdi; + port_event_t *pevt = &pevtlist[i]; + int fd = (int) pevt->portev_object; + + check_evportop(epdp); + check_event(pevt); + epdp->ed_pending[i] = fd; + + /* + * Figure out what kind of event it was + * (because we have to pass this to the callback) + */ + res = 0; + if (pevt->portev_events & POLLIN) + res |= EV_READ; + if (pevt->portev_events & POLLOUT) + res |= EV_WRITE; + + assert(epdp->ed_nevents > fd); + fdi = &(epdp->ed_fds[fd]); + + /* + * 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)) { + event_active(ev, res, 1); + } + + if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) { + event_active(ev, res, 1); + } + } /* end of all events gotten */ + + check_evportop(epdp); + + return (0); +} + + +/* + * Adds the given event (so that you will be notified when it happens via + * the callback function). + */ + +static int +evport_add(void *arg, struct event *ev) +{ + struct evport_data *evpd = arg; + struct fd_info *fdi; + int factor; + + check_evportop(evpd); + + /* + * Delegate, if it's not ours to handle. + */ + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(ev)); + + /* + * If necessary, grow the file descriptor info table + */ + + factor = 1; + while (ev->ev_fd >= factor * evpd->ed_nevents) + factor *= 2; + + if (factor > 1) { + if (-1 == grow(evpd, factor)) { + return (-1); + } + } + + fdi = &evpd->ed_fds[ev->ev_fd]; + if (ev->ev_events & EV_READ) + fdi->fdi_revt = ev; + if (ev->ev_events & EV_WRITE) + fdi->fdi_wevt = ev; + + return reassociate(evpd, fdi, ev->ev_fd); +} + +/* + * Removes the given event from the list of events to wait for. + */ + +static int +evport_del(void *arg, struct event *ev) +{ + struct evport_data *evpd = arg; + struct fd_info *fdi; + int i; + int associated = 1; + + check_evportop(evpd); + + /* + * Delegate, if it's not ours to handle + */ + if (ev->ev_events & EV_SIGNAL) { + return (evsignal_del(ev)); + } + + if (evpd->ed_nevents < ev->ev_fd) { + 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) + fdi->fdi_revt = NULL; + if (ev->ev_events & EV_WRITE) + fdi->fdi_wevt = NULL; + + 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; +} + + +static void +evport_dealloc(struct event_base *base, void *arg) +{ + struct evport_data *evpd = arg; + + evsignal_dealloc(base); + + close(evpd->ed_port); + + if (evpd->ed_fds) + free(evpd->ed_fds); + free(evpd); +} diff --git a/libevent/evrpc-internal.h b/libevent/evrpc-internal.h new file mode 100644 index 00000000000..c900f959f97 --- /dev/null +++ b/libevent/evrpc-internal.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2006 Niels Provos + * 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_ */ diff --git a/libevent/evrpc.c b/libevent/evrpc.c new file mode 100644 index 00000000000..8b3b071d0bf --- /dev/null +++ b/libevent/evrpc.c @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +#include +#ifndef WIN32 +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +#include +#ifndef WIN32 +#include +#endif +#ifndef HAVE_TAILQFOREACH +#include +#endif + +#include +#include +#include +#include + +#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, EVRPC_INPUT, hook)); + } + while ((hook = TAILQ_FIRST(&base->output_hooks)) != NULL) { + assert(evrpc_remove_hook(base, EVRPC_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 EVRPC_INPUT: + head = &base->in_hooks; + break; + case EVRPC_OUTPUT: + head = &base->out_hooks; + break; + default: + assert(hook_type == EVRPC_INPUT || hook_type == EVRPC_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 EVRPC_INPUT: + head = &base->in_hooks; + break; + case EVRPC_OUTPUT: + head = &base->out_hooks; + break; + default: + assert(hook_type == EVRPC_INPUT || hook_type == EVRPC_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, EVRPC_INPUT, hook)); + } + + while ((hook = TAILQ_FIRST(&pool->output_hooks)) != NULL) { + assert(evrpc_remove_hook(pool, EVRPC_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); +} diff --git a/libevent/evrpc.h b/libevent/evrpc.h new file mode 100644 index 00000000000..7c16b95c775 --- /dev/null +++ b/libevent/evrpc.h @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2006 Niels Provos + * 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 { + EVRPC_INPUT, /**< apply the function to an input hook */ + EVRPC_OUTPUT /**< apply the function to an output hook */ +}; + +#ifndef WIN32 +/** Deprecated alias for EVRPC_INPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define INPUT EVRPC_INPUT +/** Deprecated alias for EVRPC_OUTPUT. Not available on windows, where it + * conflicts with platform headers. */ +#define OUTPUT EVRPC_OUTPUT +#endif + +/** 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_ */ diff --git a/libevent/evsignal.h b/libevent/evsignal.h new file mode 100644 index 00000000000..9b0405eea09 --- /dev/null +++ b/libevent/evsignal.h @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2002 Niels Provos + * 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 _EVSIGNAL_H_ +#define _EVSIGNAL_H_ + +#include + +typedef void (*ev_sighandler_t)(int); + +struct evsignal_info { + struct event ev_signal; + int ev_signal_pair[2]; + int ev_signal_added; + volatile sig_atomic_t evsignal_caught; + struct event_list evsigevents[NSIG]; + sig_atomic_t evsigcaught[NSIG]; +#ifdef HAVE_SIGACTION + struct sigaction **sh_old; +#else + ev_sighandler_t **sh_old; +#endif + int sh_old_max; +}; +int evsignal_init(struct event_base *); +void evsignal_process(struct event_base *); +int evsignal_add(struct event *); +int evsignal_del(struct event *); +void evsignal_dealloc(struct event_base *); + +#endif /* _EVSIGNAL_H_ */ diff --git a/libevent/evutil.c b/libevent/evutil.c new file mode 100644 index 00000000000..7d22d3eac16 --- /dev/null +++ b/libevent/evutil.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2007 Niels Provos + * 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 +#include +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#endif + +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#if defined WIN32 && !defined(HAVE_GETTIMEOFDAY_H) +#include +#endif +#include + +#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 +} + +#ifndef HAVE_GETTIMEOFDAY +int +evutil_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct _timeb tb; + + if(tv == NULL) + return -1; + + _ftime(&tb); + tv->tv_sec = (long) tb.time; + tv->tv_usec = ((int) tb.millitm) * 1000; + return 0; +} +#endif + +int +evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +{ + int r; + va_list ap; + va_start(ap, format); + r = evutil_vsnprintf(buf, buflen, format, ap); + va_end(ap); + return r; +} + +int +evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap) +{ +#ifdef _MSC_VER + int r = _vsnprintf(buf, buflen, format, ap); + buf[buflen-1] = '\0'; + if (r >= 0) + return r; + else + return _vscprintf(format, ap); +#else + int r = vsnprintf(buf, buflen, format, ap); + buf[buflen-1] = '\0'; + return r; +#endif +} diff --git a/libevent/evutil.h b/libevent/evutil.h new file mode 100644 index 00000000000..ea751ddf7b7 --- /dev/null +++ b/libevent/evutil.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2007 Niels Provos + * 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 +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#elif defined(HAVE_INTTYPES_H) +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +#ifdef HAVE_UINT64_T +#define ev_uint64_t uint64_t +#define ev_int64_t int64_t +#elif defined(WIN32) +#define ev_uint64_t unsigned __int64 +#define ev_int64_t signed __int64 +#elif SIZEOF_LONG_LONG == 8 +#define ev_uint64_t unsigned long long +#define ev_int64_t long long +#elif 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 HAVE_UINT32_T +#define ev_uint32_t uint32_t +#elif defined(WIN32) +#define ev_uint32_t unsigned int +#elif SIZEOF_LONG == 4 +#define ev_uint32_t unsigned long +#elif SIZEOF_INT == 4 +#define ev_uint32_t unsigned int +#else +#error "No way to define ev_uint32_t" +#endif + +#ifdef HAVE_UINT16_T +#define ev_uint16_t uint16_t +#elif defined(WIN32) +#define ev_uint16_t unsigned short +#elif SIZEOF_INT == 2 +#define ev_uint16_t unsigned int +#elif SIZEOF_SHORT == 2 +#define ev_uint16_t unsigned short +#else +#error "No way to define ev_uint16_t" +#endif + +#ifdef 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 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 /* !HAVE_HAVE_TIMERADD */ + +#ifdef HAVE_TIMERCLEAR +#define evutil_timerclear(tvp) timerclear(tvp) +#else +#define evutil_timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif + +#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)) + +#ifdef 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 HAVE_GETTIMEOFDAY +#define evutil_gettimeofday(tv, tz) gettimeofday((tv), (tz)) +#else +int evutil_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +int evutil_snprintf(char *buf, size_t buflen, const char *format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif + ; +int evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap); + +#ifdef __cplusplus +} +#endif + +#endif /* _EVUTIL_H_ */ diff --git a/libevent/http-internal.h b/libevent/http-internal.h new file mode 100644 index 00000000000..9cd03cdd2bc --- /dev/null +++ b/libevent/http-internal.h @@ -0,0 +1,154 @@ +/* + * Copyright 2001 Niels Provos + * All rights reserved. + * + * This header file contains definitions for dealing with HTTP requests + * that are internal to libevent. As user of the library, you should not + * need to know about these. + */ + +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#define HTTP_CONNECT_TIMEOUT 45 +#define HTTP_WRITE_TIMEOUT 50 +#define HTTP_READ_TIMEOUT 50 + +#define HTTP_PREFIX "http://" +#define HTTP_DEFAULTPORT 80 + +enum message_read_status { + ALL_DATA_READ = 1, + MORE_DATA_EXPECTED = 0, + DATA_CORRUPTED = -1, + REQUEST_CANCELED = -2 +}; + +enum evhttp_connection_error { + EVCON_HTTP_TIMEOUT, + EVCON_HTTP_EOF, + EVCON_HTTP_INVALID_HEADER +}; + +struct evbuffer; +struct addrinfo; +struct evhttp_request; + +/* A stupid connection object - maybe make this a bufferevent later */ + +enum evhttp_connection_state { + EVCON_DISCONNECTED, /**< not currently connected not trying either*/ + EVCON_CONNECTING, /**< tries to currently connect */ + EVCON_IDLE, /**< connection is established */ + EVCON_READING_FIRSTLINE,/**< reading Request-Line (incoming conn) or + **< Status-Line (outgoing conn) */ + EVCON_READING_HEADERS, /**< reading request/response headers */ + EVCON_READING_BODY, /**< reading request/response body */ + EVCON_READING_TRAILER, /**< reading request/response chunked trailer */ + EVCON_WRITING /**< writing request/response headers/body */ +}; + +struct event_base; + +struct evhttp_connection { + /* we use tailq only if they were created for an http server */ + TAILQ_ENTRY(evhttp_connection) (next); + + int fd; + struct event ev; + struct event close_ev; + struct evbuffer *input_buffer; + struct evbuffer *output_buffer; + + char *bind_address; /* address to use for binding the src */ + u_short bind_port; /* local port for binding the src */ + + char *address; /* address to connect to */ + u_short port; + + int flags; +#define EVHTTP_CON_INCOMING 0x0001 /* only one request on it ever */ +#define EVHTTP_CON_OUTGOING 0x0002 /* multiple requests possible */ +#define EVHTTP_CON_CLOSEDETECT 0x0004 /* detecting if persistent close */ + + int timeout; /* timeout in seconds for events */ + int retry_cnt; /* retry count */ + int retry_max; /* maximum number of retries */ + + enum evhttp_connection_state state; + + /* for server connections, the http server they are connected with */ + struct evhttp *http_server; + + TAILQ_HEAD(evcon_requestq, evhttp_request) requests; + + 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 { + TAILQ_ENTRY(evhttp_cb) next; + + char *what; + + void (*cb)(struct evhttp_request *req, void *); + void *cbarg; +}; + +/* both the http server as well as the rpc system need to queue connections */ +TAILQ_HEAD(evconq, evhttp_connection); + +/* each bound socket is stored in one of these */ +struct evhttp_bound_socket { + TAILQ_ENTRY(evhttp_bound_socket) (next); + + struct event bind_ev; +}; + +struct evhttp { + TAILQ_HEAD(boundq, evhttp_bound_socket) sockets; + + TAILQ_HEAD(httpcbq, evhttp_cb) callbacks; + 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 */ +void evhttp_connection_reset(struct evhttp_connection *); + +/* connects if necessary */ +int evhttp_connection_connect(struct evhttp_connection *); + +/* notifies the current request that it failed; resets connection */ +void evhttp_connection_fail(struct evhttp_connection *, + enum evhttp_connection_error error); + +void evhttp_get_request(struct evhttp *, int, struct sockaddr *, socklen_t); + +int evhttp_hostportfile(char *, char **, u_short *, char **); + +int evhttp_parse_firstline(struct evhttp_request *, struct evbuffer*); +int evhttp_parse_headers(struct evhttp_request *, struct evbuffer*); + +void evhttp_start_read(struct evhttp_connection *); +void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *); + +void evhttp_write_buffer(struct evhttp_connection *, + void (*)(struct evhttp_connection *, void *), void *); + +/* response sending HTML the data in the buffer */ +void evhttp_response_code(struct evhttp_request *, int, const char *); +void evhttp_send_page(struct evhttp_request *, struct evbuffer *); + +#endif /* _HTTP_H */ diff --git a/libevent/http.c b/libevent/http.c new file mode 100644 index 00000000000..871bc2e4d0c --- /dev/null +++ b/libevent/http.c @@ -0,0 +1,2830 @@ +/* + * Copyright (c) 2002-2006 Niels Provos + * 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 HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_IOCCOM_H +#include +#endif + +#ifndef WIN32 +#include +#include +#include +#include +#endif + +#include + +#ifndef HAVE_TAILQFOREACH +#include +#endif + +#ifndef WIN32 +#include +#include +#endif + +#ifdef WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#undef timeout_pending +#undef timeout_initialized + +#include "strlcpy-internal.h" +#include "event.h" +#include "evhttp.h" +#include "evutil.h" +#include "log.h" +#include "http-internal.h" + +#ifdef WIN32 +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define strdup _strdup +#endif + +#ifndef HAVE_GETNAMEINFO +#define NI_MAXSERV 32 +#define NI_MAXHOST 1025 + +#define NI_NUMERICHOST 1 +#define NI_NUMERICSERV 2 + +static int +fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + + if (serv != NULL) { + char tmpserv[16]; + evutil_snprintf(tmpserv, sizeof(tmpserv), + "%d", ntohs(sin->sin_port)); + if (strlcpy(serv, tmpserv, servlen) >= servlen) + return (-1); + } + + if (host != NULL) { + if (flags & NI_NUMERICHOST) { + if (strlcpy(host, inet_ntoa(sin->sin_addr), + hostlen) >= hostlen) + return (-1); + else + return (0); + } else { + struct hostent *hp; + hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == NULL) + return (-2); + + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) + return (-1); + else + return (0); + } + } + return (0); +} + +#endif + +#ifndef HAVE_GETADDRINFO +struct addrinfo { + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +static int +fake_getaddrinfo(const char *hostname, struct addrinfo *ai) +{ + 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 = sizeof(struct sockaddr_in); + if (NULL == (ai->ai_addr = malloc(ai->ai_addrlen))) + return (-1); + 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); +} +static void +fake_freeaddrinfo(struct addrinfo *ai) +{ + free(ai->ai_addr); +} +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +/* 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 socket_connect(int fd, const char *address, unsigned short port); +static int bind_socket_ai(struct addrinfo *, int reuse); +static int bind_socket(const char *, u_short, int reuse); +static void name_from_addr(struct sockaddr *, socklen_t, char **, char **); +static int evhttp_associate_new_request_with_connection( + struct evhttp_connection *evcon); +static void evhttp_connection_start_detectclose( + struct evhttp_connection *evcon); +static void evhttp_connection_stop_detectclose( + struct evhttp_connection *evcon); +static void evhttp_request_dispatch(struct evhttp_connection* evcon); +static void evhttp_read_firstline(struct evhttp_connection *evcon, + struct evhttp_request *req); +static void evhttp_read_header(struct evhttp_connection *evcon, + struct evhttp_request *req); +static int evhttp_add_header_internal(struct evkeyvalq *headers, + const char *key, const char *value); +static int evhttp_decode_uri_internal(const char *uri, size_t length, + char *ret, int always_decode_plus); + +void evhttp_read(int, short, void *); +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) { + *d = '\0'; + *s = d + 1; + } else + *s = NULL; + return tok; +} +#endif + +static const char * +html_replace(char ch, char *buf) +{ + switch (ch) { + case '<': + return "<"; + case '>': + return ">"; + case '"': + return """; + case '\'': + return "'"; + case '&': + return "&"; + default: + break; + } + + /* Echo the character back */ + buf[0] = ch; + buf[1] = '\0'; + + return buf; +} + +/* + * Replaces <, >, ", ' and & with <, >, ", + * ' and & correspondingly. + * + * The returned string needs to be freed by the caller. + */ + +char * +evhttp_htmlescape(const char *html) +{ + 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], 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], scratch_space); + /* this is length checked */ + strcpy(p, replaced); + p += strlen(replaced); + } + + *p = '\0'; + + return (escaped_html); +} + +static const char * +evhttp_method(enum evhttp_cmd_type type) +{ + const char *method; + + switch (type) { + case EVHTTP_REQ_GET: + method = "GET"; + break; + case EVHTTP_REQ_POST: + method = "POST"; + break; + case EVHTTP_REQ_HEAD: + method = "HEAD"; + break; + default: + method = NULL; + break; + } + + return (method); +} + +static void +evhttp_add_event(struct event *ev, int timeout, int default_timeout) +{ + if (timeout != 0) { + struct timeval tv; + + evutil_timerclear(&tv); + tv.tv_sec = timeout != -1 ? timeout : default_timeout; + event_add(ev, &tv); + } else { + event_add(ev, NULL); + } +} + +void +evhttp_write_buffer(struct evhttp_connection *evcon, + void (*cb)(struct evhttp_connection *, void *), void *arg) +{ + event_debug(("%s: preparing to write buffer\n", __func__)); + + /* Set call back */ + evcon->cb = cb; + evcon->cb_arg = arg; + + /* check if the event is already pending */ + if (event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) + 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); +} + +static int +evhttp_connected(struct evhttp_connection *evcon) +{ + switch (evcon->state) { + case EVCON_DISCONNECTED: + case EVCON_CONNECTING: + return (0); + case EVCON_IDLE: + case EVCON_READING_FIRSTLINE: + case EVCON_READING_HEADERS: + case EVCON_READING_BODY: + case EVCON_READING_TRAILER: + case EVCON_WRITING: + default: + return (1); + } +} + +/* + * Create the headers needed for an HTTP request + */ +static void +evhttp_make_header_request(struct evhttp_connection *evcon, + struct evhttp_request *req) +{ + const char *method; + + evhttp_remove_header(req->output_headers, "Proxy-Connection"); + + /* Generate request line */ + method = evhttp_method(req->type); + evbuffer_add_printf(evcon->output_buffer, "%s %s HTTP/%d.%d\r\n", + method, req->uri, req->major, req->minor); + + /* Add the content length on a post request if missing */ + if (req->type == EVHTTP_REQ_POST && + evhttp_find_header(req->output_headers, "Content-Length") == NULL){ + char size[12]; + evutil_snprintf(size, sizeof(size), "%ld", + (long)EVBUFFER_LENGTH(req->output_buffer)); + evhttp_add_header(req->output_headers, "Content-Length", size); + } +} + +static int +evhttp_is_connection_close(int flags, struct evkeyvalq* headers) +{ + if (flags & EVHTTP_PROXY_REQUEST) { + /* proxy connection */ + const char *connection = evhttp_find_header(headers, "Proxy-Connection"); + return (connection == NULL || strcasecmp(connection, "keep-alive") != 0); + } else { + const char *connection = evhttp_find_header(headers, "Connection"); + return (connection != NULL && strcasecmp(connection, "close") == 0); + } +} + +static int +evhttp_is_connection_keepalive(struct evkeyvalq* headers) +{ + const char *connection = evhttp_find_header(headers, "Connection"); + return (connection != NULL + && 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]; + evutil_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) +{ + int is_keepalive = evhttp_is_connection_keepalive(req->input_headers); + evbuffer_add_printf(evcon->output_buffer, "HTTP/%d.%d %d %s\r\n", + req->major, req->minor, req->response_code, + req->response_code_line); + + if (req->major == 1) { + if (req->minor == 1) + evhttp_maybe_add_date_header(req->output_headers); + + /* + * if the protocol is 1.0; and the connection was keep-alive + * we need to add a keep-alive header, too. + */ + if (req->minor == 0 && is_keepalive) + evhttp_add_header(req->output_headers, + "Connection", "keep-alive"); + + if (req->minor == 1 || is_keepalive) { + /* + * 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, + "Content-Type") == NULL) { + evhttp_add_header(req->output_headers, + "Content-Type", "text/html; charset=ISO-8859-1"); + } + } + + /* if the request asked for a close, we send a close, too */ + if (evhttp_is_connection_close(req->flags, req->input_headers)) { + evhttp_remove_header(req->output_headers, "Connection"); + if (!(req->flags & EVHTTP_PROXY_REQUEST)) + evhttp_add_header(req->output_headers, "Connection", "close"); + evhttp_remove_header(req->output_headers, "Proxy-Connection"); + } +} + +void +evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + struct evkeyval *header; + + /* + * Depending if this is a HTTP request or response, we might need to + * add some new headers or remove existing headers. + */ + if (req->kind == EVHTTP_REQUEST) { + evhttp_make_header_request(evcon, req); + } else { + evhttp_make_header_response(evcon, req); + } + + TAILQ_FOREACH(header, req->output_headers, next) { + evbuffer_add_printf(evcon->output_buffer, "%s: %s\r\n", + header->key, header->value); + } + evbuffer_add(evcon->output_buffer, "\r\n", 2); + + if (EVBUFFER_LENGTH(req->output_buffer) > 0) { + /* + * For a request, we add the POST data, for a reply, this + * is the regular data. + */ + evbuffer_add_buffer(evcon->output_buffer, req->output_buffer); + } +} + +/* Separated host, port and file from URI */ + +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; + const char *p2; + int len; + u_short port; + + len = strlen(HTTP_PREFIX); + if (strncasecmp(url, HTTP_PREFIX, len)) + return (-1); + + url += len; + + /* We might overrun */ + if (strlcpy(host, url, sizeof (host)) >= sizeof(host)) + return (-1); + + p = strchr(host, '/'); + if (p != NULL) { + *p = '\0'; + p2 = p + 1; + } else + p2 = NULL; + + if (pfile != NULL) { + /* Generate request file */ + if (p2 == NULL) + p2 = ""; + evutil_snprintf(file, sizeof(file), "/%s", p2); + } + + p = strchr(host, ':'); + if (p != NULL) { + *p = '\0'; + port = atoi(p + 1); + + if (port == 0) + return (-1); + } else + port = HTTP_DEFAULTPORT; + + if (phost != NULL) + *phost = host; + if (pport != NULL) + *pport = port; + if (pfile != NULL) + *pfile = file; + + return (0); +} + +static int +evhttp_connection_incoming_fail(struct evhttp_request *req, + enum evhttp_connection_error error) +{ + switch (error) { + case EVCON_HTTP_TIMEOUT: + case EVCON_HTTP_EOF: + /* + * these are cases in which we probably should just + * close the connection and not send a reply. this + * case may happen when a browser keeps a persistent + * connection open and we timeout on the read. + */ + return (-1); + case EVCON_HTTP_INVALID_HEADER: + default: /* xxx: probably should just error on default */ + /* the callback looks at the uri to determine errors */ + if (req->uri) { + free(req->uri); + req->uri = NULL; + } + + /* + * the callback needs to send a reply, once the reply has + * been send, the connection should get freed. + */ + (*req->cb)(req, req->cb_arg); + } + + return (0); +} + +void +evhttp_connection_fail(struct evhttp_connection *evcon, + enum evhttp_connection_error error) +{ + struct evhttp_request* req = TAILQ_FIRST(&evcon->requests); + void (*cb)(struct evhttp_request *, void *); + void *cb_arg; + assert(req != NULL); + + if (evcon->flags & EVHTTP_CON_INCOMING) { + /* + * for incoming requests, there are two different + * failure cases. it's either a network level error + * or an http layer error. for problems on the network + * layer like timeouts we just drop the connections. + * For HTTP problems, we might have to send back a + * reply before the connection can be freed. + */ + if (evhttp_connection_incoming_fail(req, error) == -1) + evhttp_connection_free(evcon); + return; + } + + /* save the callback for later; the cb might free our object */ + cb = req->cb; + cb_arg = req->cb_arg; + + TAILQ_REMOVE(&evcon->requests, req, next); + evhttp_request_free(req); + + /* xxx: maybe we should fail all requests??? */ + + /* reset the connection */ + evhttp_connection_reset(evcon); + + /* We are trying the next request that was queued on us */ + if (TAILQ_FIRST(&evcon->requests) != NULL) + evhttp_connection_connect(evcon); + + /* inform the user */ + if (cb != NULL) + (*cb)(NULL, cb_arg); +} + +void +evhttp_write(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + int n; + + if (what == EV_TIMEOUT) { + evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); + return; + } + + n = evbuffer_write(evcon->output_buffer, fd); + if (n == -1) { + event_debug(("%s: evbuffer_write", __func__)); + evhttp_connection_fail(evcon, EVCON_HTTP_EOF); + return; + } + + if (n == 0) { + event_debug(("%s: write nothing", __func__)); + evhttp_connection_fail(evcon, EVCON_HTTP_EOF); + return; + } + + if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) { + evhttp_add_event(&evcon->ev, + evcon->timeout, HTTP_WRITE_TIMEOUT); + return; + } + + /* Activate our call back */ + if (evcon->cb != NULL) + (*evcon->cb)(evcon, evcon->cb_arg); +} + +/** + * Advance the connection state. + * - If this is an outgoing connection, we've just processed the response; + * idle or close the connection. + * - If this is an incoming connection, we've just processed the request; + * respond. + */ +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 (con_outgoing) { + /* idle or close the connection */ + int need_close; + TAILQ_REMOVE(&evcon->requests, req, next); + req->evcon = NULL; + + evcon->state = EVCON_IDLE; + + need_close = + evhttp_is_connection_close(req->flags, req->input_headers)|| + evhttp_is_connection_close(req->flags, req->output_headers); + + /* check if we got asked to close the connection */ + if (need_close) + evhttp_connection_reset(evcon); + + if (TAILQ_FIRST(&evcon->requests) != NULL) { + /* + * We have more requests; reset the connection + * and deal with the next request. + */ + if (!evhttp_connected(evcon)) + evhttp_connection_connect(evcon); + else + evhttp_request_dispatch(evcon); + } else if (!need_close) { + /* + * The connection is going to be persistent, but we + * need to detect if the other side closes it. + */ + evhttp_connection_start_detectclose(evcon); + } + } else { + /* + * incoming connection - we need to leave the request on the + * connection so that we can reply to it. + */ + evcon->state = EVCON_WRITING; + } + + /* notify the user of the request */ + (*req->cb)(req, req->cb_arg); + + /* if this was an outgoing request, we own and it's done. so free it */ + if (con_outgoing) { + evhttp_request_free(req); + } +} + +/* + * Handles reading from a chunked request. + * return ALL_DATA_READ: + * all data has been read + * return MORE_DATA_EXPECTED: + * more data is expected + * return DATA_CORRUPTED: + * data is corrupted + * return REQUEST_CANCLED: + * request was canceled by the user calling evhttp_cancel_request + */ + +static enum message_read_status +evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) +{ + int len; + + while ((len = EVBUFFER_LENGTH(buf)) > 0) { + if (req->ntoread < 0) { + /* Read chunk size */ + ev_int64_t ntoread; + char *p = evbuffer_readline(buf); + char *endp; + int error; + if (p == NULL) + break; + /* the last chunk is on a new line? */ + if (strlen(p) == 0) { + free(p); + continue; + } + ntoread = evutil_strtoll(p, &endp, 16); + error = (*p == '\0' || + (*endp != '\0' && *endp != ' ') || + ntoread < 0); + free(p); + if (error) { + /* could not get chunk size */ + return (DATA_CORRUPTED); + } + req->ntoread = ntoread; + if (req->ntoread == 0) { + /* Last chunk */ + return (ALL_DATA_READ); + } + continue; + } + + /* don't have enough to complete a chunk; wait for more */ + if (len < req->ntoread) + return (MORE_DATA_EXPECTED); + + /* Completed chunk */ + evbuffer_add(req->input_buffer, + EVBUFFER_DATA(buf), (size_t)req->ntoread); + evbuffer_drain(buf, (size_t)req->ntoread); + req->ntoread = -1; + if (req->chunk_cb != NULL) { + (*req->chunk_cb)(req, req->cb_arg); + evbuffer_drain(req->input_buffer, + EVBUFFER_LENGTH(req->input_buffer)); + } + } + + return (MORE_DATA_EXPECTED); +} + +static void +evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + struct evbuffer *buf = evcon->input_buffer; + + switch (evhttp_parse_headers(req, buf)) { + case DATA_CORRUPTED: + evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); + break; + case ALL_DATA_READ: + event_del(&evcon->ev); + evhttp_connection_done(evcon); + break; + case MORE_DATA_EXPECTED: + default: + evhttp_add_event(&evcon->ev, evcon->timeout, + HTTP_READ_TIMEOUT); + break; + } +} + +static void +evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + struct evbuffer *buf = evcon->input_buffer; + + if (req->chunked) { + switch (evhttp_handle_chunked_read(req, buf)) { + case ALL_DATA_READ: + /* finished last chunk */ + evcon->state = EVCON_READING_TRAILER; + evhttp_read_trailer(evcon, req); + return; + case DATA_CORRUPTED: + /* corrupted data */ + evhttp_connection_fail(evcon, + EVCON_HTTP_INVALID_HEADER); + return; + case REQUEST_CANCELED: + /* request canceled */ + evhttp_request_free(req); + return; + case MORE_DATA_EXPECTED: + default: + break; + } + } else if (req->ntoread < 0) { + /* Read until connection close. */ + evbuffer_add_buffer(req->input_buffer, buf); + } else if (EVBUFFER_LENGTH(buf) >= req->ntoread) { + /* Completed content length */ + evbuffer_add(req->input_buffer, EVBUFFER_DATA(buf), + (size_t)req->ntoread); + evbuffer_drain(buf, (size_t)req->ntoread); + req->ntoread = 0; + evhttp_connection_done(evcon); + return; + } + /* 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); +} + +/* + * Reads data into a buffer structure until no more data + * can be read on the file descriptor or we have read all + * the data that we wanted to read. + * Execute callback when done. + */ + +void +evhttp_read(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); + struct evbuffer *buf = evcon->input_buffer; + int n, len; + + if (what == EV_TIMEOUT) { + evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT); + return; + } + n = evbuffer_read(buf, fd, -1); + len = EVBUFFER_LENGTH(buf); + event_debug(("%s: got %d on %d\n", __func__, n, fd)); + + if (n == -1) { + if (errno != EINTR && errno != EAGAIN) { + event_debug(("%s: evbuffer_read", __func__)); + evhttp_connection_fail(evcon, EVCON_HTTP_EOF); + } else { + evhttp_add_event(&evcon->ev, evcon->timeout, + HTTP_READ_TIMEOUT); + } + return; + } else if (n == 0) { + /* Connection closed */ + evhttp_connection_done(evcon); + return; + } + + switch (evcon->state) { + case EVCON_READING_FIRSTLINE: + evhttp_read_firstline(evcon, req); + break; + case EVCON_READING_HEADERS: + evhttp_read_header(evcon, req); + break; + case EVCON_READING_BODY: + evhttp_read_body(evcon, req); + break; + case EVCON_READING_TRAILER: + evhttp_read_trailer(evcon, req); + break; + case EVCON_DISCONNECTED: + case EVCON_CONNECTING: + case EVCON_IDLE: + case EVCON_WRITING: + default: + event_errx(1, "%s: illegal connection state %d", + __func__, evcon->state); + } +} + +static void +evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg) +{ + /* This is after writing the request to the server */ + struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); + assert(req != NULL); + + assert(evcon->state == EVCON_WRITING); + + /* We are done writing our header and are now expecting the response */ + req->kind = EVHTTP_RESPONSE; + + evhttp_start_read(evcon); +} + +/* + * Clean up a connection object + */ + +void +evhttp_connection_free(struct evhttp_connection *evcon) +{ + struct evhttp_request *req; + + /* notify interested parties that this connection is going down */ + if (evcon->fd != -1) { + if (evhttp_connected(evcon) && evcon->closecb != NULL) + (*evcon->closecb)(evcon, evcon->closecb_arg); + } + + /* remove all requests that might be queued on this connection */ + while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) { + TAILQ_REMOVE(&evcon->requests, req, next); + evhttp_request_free(req); + } + + if (evcon->http_server != NULL) { + struct evhttp *http = evcon->http_server; + TAILQ_REMOVE(&http->connections, evcon, next); + } + + if (event_initialized(&evcon->close_ev)) + event_del(&evcon->close_ev); + + if (event_initialized(&evcon->ev)) + event_del(&evcon->ev); + + if (evcon->fd != -1) + EVUTIL_CLOSESOCKET(evcon->fd); + + if (evcon->bind_address != NULL) + free(evcon->bind_address); + + if (evcon->address != NULL) + free(evcon->address); + + if (evcon->input_buffer != NULL) + evbuffer_free(evcon->input_buffer); + + if (evcon->output_buffer != NULL) + evbuffer_free(evcon->output_buffer); + + 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__); +} + +void +evhttp_connection_set_local_port(struct evhttp_connection *evcon, + unsigned short port) +{ + assert(evcon->state == EVCON_DISCONNECTED); + evcon->bind_port = port; +} + +static void +evhttp_request_dispatch(struct evhttp_connection* evcon) +{ + struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); + + /* this should not usually happy but it's possible */ + if (req == NULL) + return; + + /* delete possible close detection events */ + evhttp_connection_stop_detectclose(evcon); + + /* we assume that the connection is connected already */ + assert(evcon->state == EVCON_IDLE); + + evcon->state = EVCON_WRITING; + + /* Create the header from the store arguments */ + evhttp_make_header(evcon, req); + + evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL); +} + +/* Reset our connection state */ +void +evhttp_connection_reset(struct evhttp_connection *evcon) +{ + if (event_initialized(&evcon->ev)) + event_del(&evcon->ev); + + if (evcon->fd != -1) { + /* inform interested parties about connection close */ + if (evhttp_connected(evcon) && evcon->closecb != NULL) + (*evcon->closecb)(evcon, evcon->closecb_arg); + + EVUTIL_CLOSESOCKET(evcon->fd); + evcon->fd = -1; + } + evcon->state = EVCON_DISCONNECTED; + + evbuffer_drain(evcon->input_buffer, + EVBUFFER_LENGTH(evcon->input_buffer)); + evbuffer_drain(evcon->output_buffer, + EVBUFFER_LENGTH(evcon->output_buffer)); +} + +static void +evhttp_detect_close_cb(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + evhttp_connection_reset(evcon); +} + +static void +evhttp_connection_start_detectclose(struct evhttp_connection *evcon) +{ + evcon->flags |= EVHTTP_CON_CLOSEDETECT; + + if (event_initialized(&evcon->close_ev)) + 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); +} + +static void +evhttp_connection_stop_detectclose(struct evhttp_connection *evcon) +{ + evcon->flags &= ~EVHTTP_CON_CLOSEDETECT; + event_del(&evcon->close_ev); +} + +static void +evhttp_connection_retry(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + + evcon->state = EVCON_DISCONNECTED; + evhttp_connection_connect(evcon); +} + +/* + * Call back for asynchronous connection attempt. + */ + +static void +evhttp_connectioncb(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + int error; + socklen_t errsz = sizeof(error); + + if (what == EV_TIMEOUT) { + event_debug(("%s: connection timeout for \"%s:%d\" on %d", + __func__, evcon->address, evcon->port, evcon->fd)); + goto cleanup; + } + + /* Check if the connection completed */ + if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error, + &errsz) == -1) { + event_debug(("%s: getsockopt for \"%s:%d\" on %d", + __func__, evcon->address, evcon->port, evcon->fd)); + goto cleanup; + } + + if (error) { + event_debug(("%s: connect failed for \"%s:%d\" on %d: %s", + __func__, evcon->address, evcon->port, evcon->fd, + strerror(error))); + goto cleanup; + } + + /* We are connected to the server now */ + event_debug(("%s: connected to \"%s:%d\" on %d\n", + __func__, evcon->address, evcon->port, evcon->fd)); + + /* Reset the retry count as we were successful in connecting */ + evcon->retry_cnt = 0; + evcon->state = EVCON_IDLE; + + /* try to start requests that have queued up on this connection */ + evhttp_request_dispatch(evcon); + return; + + 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++; + return; + } + evhttp_connection_reset(evcon); + + /* for now, we just signal all requests by executing their callbacks */ + while (TAILQ_FIRST(&evcon->requests) != NULL) { + struct evhttp_request *request = TAILQ_FIRST(&evcon->requests); + TAILQ_REMOVE(&evcon->requests, request, next); + request->evcon = NULL; + + /* we might want to set an error here */ + request->cb(request, request->cb_arg); + evhttp_request_free(request); + } +} + +/* + * Check if we got a valid response code. + */ + +static int +evhttp_valid_response_code(int code) +{ + if (code == 0) + return (0); + + return (1); +} + +/* Parses the status line of a web server */ + +static int +evhttp_parse_response_line(struct evhttp_request *req, char *line) +{ + char *protocol; + char *number; + char *readable; + + protocol = strsep(&line, " "); + if (line == NULL) + return (-1); + number = strsep(&line, " "); + if (line == NULL) + return (-1); + readable = line; + + if (strcmp(protocol, "HTTP/1.0") == 0) { + req->major = 1; + req->minor = 0; + } else if (strcmp(protocol, "HTTP/1.1") == 0) { + req->major = 1; + req->minor = 1; + } else { + event_debug(("%s: bad protocol \"%s\"", + __func__, protocol)); + return (-1); + } + + req->response_code = atoi(number); + if (!evhttp_valid_response_code(req->response_code)) { + event_debug(("%s: bad response code \"%s\"", + __func__, number)); + return (-1); + } + + if ((req->response_code_line = strdup(readable)) == NULL) + event_err(1, "%s: strdup", __func__); + + return (0); +} + +/* Parse the first line of a HTTP request */ + +static int +evhttp_parse_request_line(struct evhttp_request *req, char *line) +{ + char *method; + char *uri; + char *version; + + /* Parse the request line */ + method = strsep(&line, " "); + if (line == NULL) + return (-1); + uri = strsep(&line, " "); + if (line == NULL) + return (-1); + version = strsep(&line, " "); + if (line != NULL) + return (-1); + + /* First line */ + if (strcmp(method, "GET") == 0) { + req->type = EVHTTP_REQ_GET; + } else if (strcmp(method, "POST") == 0) { + req->type = EVHTTP_REQ_POST; + } else if (strcmp(method, "HEAD") == 0) { + req->type = EVHTTP_REQ_HEAD; + } else { + event_debug(("%s: bad method %s on request %p from %s", + __func__, method, req, req->remote_host)); + return (-1); + } + + if (strcmp(version, "HTTP/1.0") == 0) { + req->major = 1; + req->minor = 0; + } else if (strcmp(version, "HTTP/1.1") == 0) { + req->major = 1; + req->minor = 1; + } else { + event_debug(("%s: bad version %s on request %p from %s", + __func__, version, req, req->remote_host)); + return (-1); + } + + if ((req->uri = strdup(uri)) == NULL) { + event_debug(("%s: evhttp_decode_uri", __func__)); + return (-1); + } + + /* determine if it's a proxy request */ + if (strlen(req->uri) > 0 && req->uri[0] != '/') + req->flags |= EVHTTP_PROXY_REQUEST; + + return (0); +} + +const char * +evhttp_find_header(const struct evkeyvalq *headers, const char *key) +{ + struct evkeyval *header; + + TAILQ_FOREACH(header, headers, next) { + if (strcasecmp(header->key, key) == 0) + return (header->value); + } + + return (NULL); +} + +void +evhttp_clear_headers(struct evkeyvalq *headers) +{ + struct evkeyval *header; + + for (header = TAILQ_FIRST(headers); + header != NULL; + header = TAILQ_FIRST(headers)) { + TAILQ_REMOVE(headers, header, next); + free(header->key); + free(header->value); + free(header); + } +} + +/* + * Returns 0, if the header was successfully removed. + * Returns -1, if the header could not be found. + */ + +int +evhttp_remove_header(struct evkeyvalq *headers, const char *key) +{ + struct evkeyval *header; + + TAILQ_FOREACH(header, headers, next) { + if (strcasecmp(header->key, key) == 0) + break; + } + + if (header == NULL) + return (-1); + + /* Free and remove the header that we found */ + TAILQ_REMOVE(headers, header, next); + free(header->key); + free(header->value); + free(header); + + return (0); +} + +static int +evhttp_header_is_valid_value(const char *value) +{ + const char *p = value; + + while ((p = strpbrk(p, "\r\n")) != NULL) { + /* we really expect only one new line */ + p += strspn(p, "\r\n"); + /* we expect a space or tab for continuation */ + if (*p != ' ' && *p != '\t') + return (0); + } + return (1); +} + +int +evhttp_add_header(struct evkeyvalq *headers, + const char *key, const char *value) +{ + event_debug(("%s: key: %s val: %s\n", __func__, key, value)); + + if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) { + /* drop illegal headers */ + event_debug(("%s: dropping illegal header key\n", __func__)); + return (-1); + } + + if (!evhttp_header_is_valid_value(value)) { + event_debug(("%s: dropping illegal header value\n", __func__)); + return (-1); + } + + return (evhttp_add_header_internal(headers, key, value)); +} + +static int +evhttp_add_header_internal(struct evkeyvalq *headers, + const char *key, const char *value) +{ + struct evkeyval *header = calloc(1, sizeof(struct evkeyval)); + if (header == NULL) { + event_warn("%s: calloc", __func__); + return (-1); + } + if ((header->key = strdup(key)) == NULL) { + free(header); + event_warn("%s: strdup", __func__); + return (-1); + } + if ((header->value = strdup(value)) == NULL) { + free(header->key); + free(header); + event_warn("%s: strdup", __func__); + return (-1); + } + + TAILQ_INSERT_TAIL(headers, header, next); + + return (0); +} + +/* + * Parses header lines from a request or a response into the specified + * request object given an event buffer. + * + * Returns + * DATA_CORRUPTED on error + * MORE_DATA_EXPECTED when we need to read more headers + * ALL_DATA_READ when all headers have been read. + */ + +enum message_read_status +evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer) +{ + char *line; + enum message_read_status status = ALL_DATA_READ; + + line = evbuffer_readline(buffer); + if (line == NULL) + return (MORE_DATA_EXPECTED); + + switch (req->kind) { + case EVHTTP_REQUEST: + if (evhttp_parse_request_line(req, line) == -1) + status = DATA_CORRUPTED; + break; + case EVHTTP_RESPONSE: + if (evhttp_parse_response_line(req, line) == -1) + status = DATA_CORRUPTED; + break; + default: + status = DATA_CORRUPTED; + } + + free(line); + return (status); +} + +static int +evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line) +{ + struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq); + char *newval; + size_t old_len, line_len; + + if (header == NULL) + return (-1); + + old_len = strlen(header->value); + line_len = strlen(line); + + newval = realloc(header->value, old_len + line_len + 1); + if (newval == NULL) + return (-1); + + memcpy(newval + old_len, line, line_len + 1); + header->value = newval; + + return (0); +} + +enum message_read_status +evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer) +{ + char *line; + enum message_read_status status = MORE_DATA_EXPECTED; + + struct evkeyvalq* headers = req->input_headers; + while ((line = evbuffer_readline(buffer)) + != NULL) { + char *skey, *svalue; + + if (*line == '\0') { /* Last header - Done */ + status = ALL_DATA_READ; + free(line); + break; + } + + /* Check if this is a continuation line */ + if (*line == ' ' || *line == '\t') { + if (evhttp_append_to_last_header(headers, line) == -1) + goto error; + free(line); + continue; + } + + /* Processing of header lines */ + svalue = line; + skey = strsep(&svalue, ":"); + if (svalue == NULL) + goto error; + + svalue += strspn(svalue, " "); + + if (evhttp_add_header(headers, skey, svalue) == -1) + goto error; + + free(line); + } + + return (status); + + error: + free(line); + return (DATA_CORRUPTED); +} + +static int +evhttp_get_body_length(struct evhttp_request *req) +{ + struct evkeyvalq *headers = req->input_headers; + const char *content_length; + const char *connection; + + content_length = evhttp_find_header(headers, "Content-Length"); + connection = evhttp_find_header(headers, "Connection"); + + if (content_length == NULL && connection == NULL) + req->ntoread = -1; + else if (content_length == NULL && + strcasecmp(connection, "Close") != 0) { + /* Bad combination, we don't know when it will end */ + event_warnx("%s: we got no content length, but the " + "server wants to keep the connection open: %s.", + __func__, connection); + return (-1); + } else if (content_length == NULL) { + req->ntoread = -1; + } else { + char *endp; + ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10); + if (*content_length == '\0' || *endp != '\0' || ntoread < 0) { + event_debug(("%s: illegal content length: %s", + __func__, content_length)); + return (-1); + } + req->ntoread = ntoread; + } + + event_debug(("%s: bytes to read: %lld (in buffer %ld)\n", + __func__, req->ntoread, + EVBUFFER_LENGTH(req->evcon->input_buffer))); + + return (0); +} + +static void +evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + const char *xfer_enc; + + /* If this is a request without a body, then we are done */ + if (req->kind == EVHTTP_REQUEST && req->type != EVHTTP_REQ_POST) { + evhttp_connection_done(evcon); + return; + } + evcon->state = EVCON_READING_BODY; + xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding"); + if (xfer_enc != NULL && strcasecmp(xfer_enc, "chunked") == 0) { + req->chunked = 1; + req->ntoread = -1; + } else { + if (evhttp_get_body_length(req) == -1) { + evhttp_connection_fail(evcon, + EVCON_HTTP_INVALID_HEADER); + return; + } + } + evhttp_read_body(evcon, req); +} + +static void +evhttp_read_firstline(struct evhttp_connection *evcon, + struct evhttp_request *req) +{ + enum message_read_status res; + + res = evhttp_parse_firstline(req, evcon->input_buffer); + if (res == DATA_CORRUPTED) { + /* Error while reading, terminate */ + event_debug(("%s: bad header lines on %d\n", + __func__, evcon->fd)); + evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); + return; + } else if (res == MORE_DATA_EXPECTED) { + /* Need more header lines */ + evhttp_add_event(&evcon->ev, + evcon->timeout, HTTP_READ_TIMEOUT); + return; + } + + evcon->state = EVCON_READING_HEADERS; + evhttp_read_header(evcon, req); +} + +static void +evhttp_read_header(struct evhttp_connection *evcon, struct evhttp_request *req) +{ + enum message_read_status res; + int fd = evcon->fd; + + res = evhttp_parse_headers(req, evcon->input_buffer); + if (res == DATA_CORRUPTED) { + /* Error while reading, terminate */ + event_debug(("%s: bad header lines on %d\n", __func__, fd)); + evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); + return; + } else if (res == MORE_DATA_EXPECTED) { + /* Need more header lines */ + evhttp_add_event(&evcon->ev, + evcon->timeout, HTTP_READ_TIMEOUT); + return; + } + + /* Done reading headers, do the real work */ + switch (req->kind) { + case EVHTTP_REQUEST: + event_debug(("%s: checking for post data on %d\n", + __func__, fd)); + evhttp_get_body(evcon, req); + break; + + case EVHTTP_RESPONSE: + if (req->response_code == HTTP_NOCONTENT || + req->response_code == HTTP_NOTMODIFIED || + (req->response_code >= 100 && req->response_code < 200)) { + event_debug(("%s: skipping body for code %d\n", + __func__, req->response_code)); + evhttp_connection_done(evcon); + } else { + event_debug(("%s: start of read body for %s on %d\n", + __func__, req->remote_host, fd)); + evhttp_get_body(evcon, req); + } + break; + + default: + event_warnx("%s: bad header on %d", __func__, fd); + evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER); + break; + } +} + +/* + * Creates a TCP connection to the specified port and executes a callback + * when finished. Failure or sucess is indicate by the passed connection + * object. + * + * Although this interface accepts a hostname, it is intended to take + * only numeric hostnames so that non-blocking DNS resolution can + * happen elsewhere. + */ + +struct evhttp_connection * +evhttp_connection_new(const char *address, unsigned short port) +{ + struct evhttp_connection *evcon = NULL; + + event_debug(("Attempting connection to %s:%d\n", address, port)); + + if ((evcon = calloc(1, sizeof(struct evhttp_connection))) == NULL) { + event_warn("%s: calloc failed", __func__); + goto error; + } + + evcon->fd = -1; + evcon->port = port; + + evcon->timeout = -1; + evcon->retry_cnt = evcon->retry_max = 0; + + if ((evcon->address = strdup(address)) == NULL) { + event_warn("%s: strdup failed", __func__); + goto error; + } + + if ((evcon->input_buffer = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new failed", __func__); + goto error; + } + + if ((evcon->output_buffer = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new failed", __func__); + goto error; + } + + evcon->state = EVCON_DISCONNECTED; + TAILQ_INIT(&evcon->requests); + + return (evcon); + + error: + if (evcon != NULL) + evhttp_connection_free(evcon); + 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) +{ + evcon->timeout = timeout_in_secs; +} + +void +evhttp_connection_set_retries(struct evhttp_connection *evcon, + int retry_max) +{ + evcon->retry_max = retry_max; +} + +void +evhttp_connection_set_closecb(struct evhttp_connection *evcon, + void (*cb)(struct evhttp_connection *, void *), void *cbarg) +{ + evcon->closecb = cb; + evcon->closecb_arg = cbarg; +} + +void +evhttp_connection_get_peer(struct evhttp_connection *evcon, + char **address, u_short *port) +{ + *address = evcon->address; + *port = evcon->port; +} + +int +evhttp_connection_connect(struct evhttp_connection *evcon) +{ + if (evcon->state == EVCON_CONNECTING) + return (0); + + evhttp_connection_reset(evcon); + + assert(!(evcon->flags & EVHTTP_CON_INCOMING)); + evcon->flags |= EVHTTP_CON_OUTGOING; + + evcon->fd = bind_socket( + evcon->bind_address, evcon->bind_port, 0 /*reuse*/); + 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; + + return (0); +} + +/* + * Starts an HTTP request on the provided evhttp_connection object. + * If the connection object is not connected to the web server already, + * this will start the connection. + */ + +int +evhttp_make_request(struct evhttp_connection *evcon, + struct evhttp_request *req, + enum evhttp_cmd_type type, const char *uri) +{ + /* We are making a request */ + req->kind = EVHTTP_REQUEST; + req->type = type; + if (req->uri != NULL) + free(req->uri); + if ((req->uri = strdup(uri)) == NULL) + event_err(1, "%s: strdup", __func__); + + /* Set the protocol version if it is not supplied */ + if (!req->major && !req->minor) { + req->major = 1; + req->minor = 1; + } + + assert(req->evcon == NULL); + req->evcon = evcon; + assert(!(req->flags & EVHTTP_REQ_OWN_CONNECTION)); + + TAILQ_INSERT_TAIL(&evcon->requests, req, next); + + /* If the connection object is not connected; make it so */ + if (!evhttp_connected(evcon)) + return (evhttp_connection_connect(evcon)); + + /* + * If it's connected already and we are the first in the queue, + * then we can dispatch this request immediately. Otherwise, it + * will be dispatched once the pending requests are completed. + */ + if (TAILQ_FIRST(&evcon->requests) == req) + evhttp_request_dispatch(evcon); + + return (0); +} + +/* + * Reads data from file descriptor into request structure + * Request structure needs to be set up correctly. + */ + +void +evhttp_start_read(struct evhttp_connection *evcon) +{ + /* Set up an event to read the headers */ + if (event_initialized(&evcon->ev)) + event_del(&evcon->ev); + 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); + evcon->state = EVCON_READING_FIRSTLINE; +} + +static void +evhttp_send_done(struct evhttp_connection *evcon, void *arg) +{ + int need_close; + struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); + TAILQ_REMOVE(&evcon->requests, req, next); + + /* delete possible close detection events */ + evhttp_connection_stop_detectclose(evcon); + + need_close = + (req->minor == 0 && + !evhttp_is_connection_keepalive(req->input_headers))|| + evhttp_is_connection_close(req->flags, req->input_headers) || + evhttp_is_connection_close(req->flags, req->output_headers); + + assert(req->flags & EVHTTP_REQ_OWN_CONNECTION); + evhttp_request_free(req); + + if (need_close) { + evhttp_connection_free(evcon); + return; + } + + /* we have a persistent connection; try to accept another request. */ + if (evhttp_associate_new_request_with_connection(evcon) == -1) + evhttp_connection_free(evcon); +} + +/* + * Returns an error page. + */ + +void +evhttp_send_error(struct evhttp_request *req, int error, const char *reason) +{ +#define ERR_FORMAT "\n" \ + "%d %s\n" \ + "\n" \ + "

Method Not Implemented

\n" \ + "Invalid method in request

\n" \ + "\n" + + struct evbuffer *buf = evbuffer_new(); + + /* close the connection on error */ + evhttp_add_header(req->output_headers, "Connection", "close"); + + evhttp_response_code(req, 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 */ + +static inline void +evhttp_send(struct evhttp_request *req, struct evbuffer *databuf) +{ + struct evhttp_connection *evcon = req->evcon; + + assert(TAILQ_FIRST(&evcon->requests) == req); + + /* xxx: not sure if we really should expose the data buffer this way */ + if (databuf != NULL) + evbuffer_add_buffer(req->output_buffer, databuf); + + /* Adds headers to the response */ + evhttp_make_header(evcon, req); + + evhttp_write_buffer(evcon, evhttp_send_done, NULL); +} + +void +evhttp_send_reply(struct evhttp_request *req, int code, const char *reason, + struct evbuffer *databuf) +{ + evhttp_response_code(req, code, reason); + + evhttp_send(req, databuf); +} + +void +evhttp_send_reply_start(struct evhttp_request *req, int code, + const char *reason) +{ + evhttp_response_code(req, code, reason); + if (req->major == 1 && req->minor == 1) { + /* use chunked encoding for HTTP/1.1 */ + evhttp_add_header(req->output_headers, "Transfer-Encoding", + "chunked"); + req->chunked = 1; + } + evhttp_make_header(req->evcon, req); + evhttp_write_buffer(req->evcon, NULL, NULL); +} + +void +evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf) +{ + if (req->chunked) { + evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", + (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); +} + +void +evhttp_send_reply_end(struct evhttp_request *req) +{ + struct evhttp_connection *evcon = req->evcon; + + if (req->chunked) { + evbuffer_add(req->evcon->output_buffer, "0\r\n\r\n", 5); + evhttp_write_buffer(req->evcon, evhttp_send_done, NULL); + req->chunked = 0; + } else if (!event_pending(&evcon->ev, EV_WRITE|EV_TIMEOUT, NULL)) { + /* let the connection know that we are done with the request */ + evhttp_send_done(evcon, NULL); + } else { + /* make the callback execute after all data has been written */ + evcon->cb = evhttp_send_done; + evcon->cb_arg = NULL; + } +} + +void +evhttp_response_code(struct evhttp_request *req, int code, const char *reason) +{ + req->kind = EVHTTP_RESPONSE; + req->response_code = code; + if (req->response_code_line != NULL) + free(req->response_code_line); + req->response_code_line = strdup(reason); +} + +void +evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf) +{ + if (!req->major || !req->minor) { + req->major = 1; + req->minor = 1; + } + + if (req->kind != EVHTTP_RESPONSE) + evhttp_response_code(req, 200, "OK"); + + evhttp_clear_headers(req->output_headers); + evhttp_add_header(req->output_headers, "Content-Type", "text/html"); + evhttp_add_header(req->output_headers, "Connection", "close"); + + evhttp_send(req, databuf); +} + +static const char uri_chars[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, + /* 64 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, + /* 128 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 192 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* + * Helper functions to encode/decode a URI. + * The returned string must be freed by the caller. + */ +char * +evhttp_encode_uri(const char *uri) +{ + struct evbuffer *buf = evbuffer_new(); + char *p; + + for (p = (char *)uri; *p != '\0'; p++) { + if (uri_chars[(u_char)(*p)]) { + evbuffer_add(buf, p, 1); + } else { + evbuffer_add_printf(buf, "%%%02X", (u_char)(*p)); + } + } + evbuffer_add(buf, "", 1); + p = strdup((char *)EVBUFFER_DATA(buf)); + evbuffer_free(buf); + + return (p); +} + +/* + * @param always_decode_plus: when true we transform plus to space even + * if we have not seen a ?. + */ +static int +evhttp_decode_uri_internal( + const char *uri, size_t length, char *ret, int always_decode_plus) +{ + char c; + int i, j, in_query = always_decode_plus; + + for (i = j = 0; uri[i] != '\0'; i++) { + c = uri[i]; + if (c == '?') { + in_query = 1; + } else if (c == '+' && in_query) { + c = ' '; + } else if (c == '%' && isxdigit((unsigned char)uri[i+1]) && + isxdigit((unsigned char)uri[i+2])) { + char tmp[] = { uri[i+1], uri[i+2], '\0' }; + c = (char)strtol(tmp, NULL, 16); + i += 2; + } + ret[j++] = c; + } + ret[j] = '\0'; + + return (j); +} + +char * +evhttp_decode_uri(const char *uri) +{ + char *ret; + + if ((ret = malloc(strlen(uri) + 1)) == NULL) + event_err(1, "%s: malloc(%lu)", __func__, + (unsigned long)(strlen(uri) + 1)); + + evhttp_decode_uri_internal(uri, strlen(uri), + ret, 0 /*always_decode_plus*/); + + return (ret); +} + +/* + * Helper function to parse out arguments in a query. + * The arguments are separated by key and value. + */ + +void +evhttp_parse_query(const char *uri, struct evkeyvalq *headers) +{ + char *line; + char *argument; + char *p; + + TAILQ_INIT(headers); + + /* No arguments - we are done */ + if (strchr(uri, '?') == NULL) + return; + + if ((line = strdup(uri)) == NULL) + event_err(1, "%s: strdup", __func__); + + + argument = line; + + /* We already know that there has to be a ? */ + strsep(&argument, "?"); + + p = argument; + while (p != NULL && *p != '\0') { + char *key, *value, *decoded_value; + argument = strsep(&p, "&"); + + value = argument; + key = strsep(&value, "="); + if (value == NULL) + goto error; + + if ((decoded_value = malloc(strlen(value) + 1)) == NULL) + event_err(1, "%s: malloc", __func__); + + evhttp_decode_uri_internal(value, strlen(value), + decoded_value, 1 /*always_decode_plus*/); + event_debug(("Query Param: %s -> %s\n", key, decoded_value)); + evhttp_add_header_internal(headers, key, decoded_value); + free(decoded_value); + } + + error: + free(line); +} + +static struct evhttp_cb * +evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req) +{ + struct evhttp_cb *cb; + size_t offset = 0; + + /* Test for different URLs */ + char *p = strchr(req->uri, '?'); + if (p != NULL) + offset = (size_t)(p - req->uri); + + TAILQ_FOREACH(cb, callbacks, next) { + int res = 0; + if (p == NULL) { + res = strcmp(cb->what, req->uri) == 0; + } else { + res = ((strncmp(cb->what, req->uri, offset) == 0) && + (cb->what[offset] == '\0')); + } + + if (res) + return (cb); + } + + return (NULL); +} + +static void +evhttp_handle_request(struct evhttp_request *req, void *arg) +{ + struct evhttp *http = arg; + struct evhttp_cb *cb = NULL; + + if (req->uri == NULL) { + evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); + return; + } + + if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) { + (*cb->cb)(req, cb->cbarg); + return; + } + + /* Generic call back */ + if (http->gencb) { + (*http->gencb)(req, http->gencbarg); + return; + } else { + /* We need to send a 404 here */ +#define ERR_FORMAT "" \ + "404 Not Found" \ + "" \ + "

Not Found

" \ + "

The requested URL %s was not found on this server.

"\ + "\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, ERR_FORMAT, escaped_html); + + free(escaped_html); + + evhttp_send_page(req, buf); + + evbuffer_free(buf); +#undef ERR_FORMAT + } +} + +static void +accept_socket(int fd, short what, void *arg) +{ + struct evhttp *http = arg; + struct sockaddr_storage ss; + socklen_t addrlen = sizeof(ss); + int nfd; + + if ((nfd = accept(fd, (struct sockaddr *)&ss, &addrlen)) == -1) { + if (errno != EAGAIN && errno != EINTR) + event_warn("%s: bad accept", __func__); + return; + } + if (evutil_make_socket_nonblocking(nfd) < 0) + return; + + evhttp_get_request(http, nfd, (struct sockaddr *)&ss, addrlen); +} + +int +evhttp_bind_socket(struct evhttp *http, const char *address, u_short port) +{ + int fd; + int res; + + if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1) + return (-1); + + if (listen(fd, 128) == -1) { + event_warn("%s: listen", __func__); + EVUTIL_CLOSESOCKET(fd); + return (-1); + } + + res = evhttp_accept_socket(http, fd); + + if (res != -1) + event_debug(("Bound to port %d - Awaiting connections ... ", + port)); + + return (res); +} + +int +evhttp_accept_socket(struct evhttp *http, int fd) +{ + struct evhttp_bound_socket *bound; + struct event *ev; + int res; + + bound = malloc(sizeof(struct evhttp_bound_socket)); + if (bound == NULL) + return (-1); + + ev = &bound->bind_ev; + + /* Schedule the socket for accepting */ + event_set(ev, fd, EV_READ | EV_PERSIST, accept_socket, http); + EVHTTP_BASE_SET(http, ev); + + res = event_add(ev, NULL); + + if (res == -1) { + free(bound); + return (-1); + } + + TAILQ_INSERT_TAIL(&http->sockets, bound, next); + + return (0); +} + +static struct evhttp* +evhttp_new_object(void) +{ + struct evhttp *http = NULL; + + if ((http = calloc(1, sizeof(struct evhttp))) == NULL) { + event_warn("%s: calloc", __func__); + return (NULL); + } + + http->timeout = -1; + + TAILQ_INIT(&http->sockets); + TAILQ_INIT(&http->callbacks); + TAILQ_INIT(&http->connections); + + 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); + } + + return (http); +} + +void +evhttp_free(struct evhttp* http) +{ + struct evhttp_cb *http_cb; + struct evhttp_connection *evcon; + struct evhttp_bound_socket *bound; + int fd; + + /* Remove the accepting part */ + while ((bound = TAILQ_FIRST(&http->sockets)) != NULL) { + TAILQ_REMOVE(&http->sockets, bound, next); + + fd = bound->bind_ev.ev_fd; + event_del(&bound->bind_ev); + EVUTIL_CLOSESOCKET(fd); + + free(bound); + } + + while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) { + /* evhttp_connection_free removes the connection */ + evhttp_connection_free(evcon); + } + + while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) { + TAILQ_REMOVE(&http->callbacks, http_cb, next); + free(http_cb->what); + free(http_cb); + } + + free(http); +} + +void +evhttp_set_timeout(struct evhttp* http, int timeout_in_secs) +{ + http->timeout = timeout_in_secs; +} + +void +evhttp_set_cb(struct evhttp *http, const char *uri, + void (*cb)(struct evhttp_request *, void *), void *cbarg) +{ + struct evhttp_cb *http_cb; + + if ((http_cb = calloc(1, sizeof(struct evhttp_cb))) == NULL) + event_err(1, "%s: calloc", __func__); + + http_cb->what = strdup(uri); + http_cb->cb = cb; + http_cb->cbarg = cbarg; + + TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next); +} + +int +evhttp_del_cb(struct evhttp *http, const char *uri) +{ + struct evhttp_cb *http_cb; + + TAILQ_FOREACH(http_cb, &http->callbacks, next) { + if (strcmp(http_cb->what, uri) == 0) + break; + } + if (http_cb == NULL) + return (-1); + + TAILQ_REMOVE(&http->callbacks, http_cb, next); + free(http_cb->what); + free(http_cb); + + return (0); +} + +void +evhttp_set_gencb(struct evhttp *http, + void (*cb)(struct evhttp_request *, void *), void *cbarg) +{ + http->gencb = cb; + http->gencbarg = cbarg; +} + +/* + * Request related functions + */ + +struct evhttp_request * +evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg) +{ + struct evhttp_request *req = NULL; + + /* Allocate request structure */ + if ((req = calloc(1, sizeof(struct evhttp_request))) == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + + req->kind = EVHTTP_RESPONSE; + req->input_headers = calloc(1, sizeof(struct evkeyvalq)); + if (req->input_headers == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + TAILQ_INIT(req->input_headers); + + req->output_headers = calloc(1, sizeof(struct evkeyvalq)); + if (req->output_headers == NULL) { + event_warn("%s: calloc", __func__); + goto error; + } + TAILQ_INIT(req->output_headers); + + if ((req->input_buffer = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new", __func__); + goto error; + } + + if ((req->output_buffer = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new", __func__); + goto error; + } + + req->cb = cb; + req->cb_arg = arg; + + return (req); + + error: + if (req != NULL) + evhttp_request_free(req); + return (NULL); +} + +void +evhttp_request_free(struct evhttp_request *req) +{ + if (req->remote_host != NULL) + free(req->remote_host); + if (req->uri != NULL) + free(req->uri); + if (req->response_code_line != NULL) + free(req->response_code_line); + + evhttp_clear_headers(req->input_headers); + free(req->input_headers); + + evhttp_clear_headers(req->output_headers); + free(req->output_headers); + + if (req->input_buffer != NULL) + evbuffer_free(req->input_buffer); + + if (req->output_buffer != NULL) + evbuffer_free(req->output_buffer); + + free(req); +} + +void +evhttp_request_set_chunked_cb(struct evhttp_request *req, + void (*cb)(struct evhttp_request *, void *)) +{ + req->chunk_cb = cb; +} + +/* + * Allows for inspection of the request URI + */ + +const char * +evhttp_request_uri(struct evhttp_request *req) { + if (req->uri == NULL) + event_debug(("%s: request %p has no uri\n", __func__, req)); + return (req->uri); +} + +/* + * Takes a file descriptor to read a request from. + * The callback is executed once the whole request has been read. + */ + +static struct evhttp_connection* +evhttp_get_request_connection( + struct evhttp* http, + int fd, struct sockaddr *sa, socklen_t salen) +{ + struct evhttp_connection *evcon; + char *hostname = NULL, *portname = NULL; + + name_from_addr(sa, salen, &hostname, &portname); + if (hostname == NULL || portname == NULL) { + if (hostname) free(hostname); + if (portname) free(portname); + return (NULL); + } + + event_debug(("%s: new request from %s:%s on %d\n", + __func__, hostname, portname, fd)); + + /* we need a connection object to put the http request on */ + evcon = evhttp_connection_new(hostname, atoi(portname)); + free(hostname); + free(portname); + if (evcon == 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_READING_FIRSTLINE; + + evcon->fd = fd; + + return (evcon); +} + +static int +evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon) +{ + struct evhttp *http = evcon->http_server; + struct evhttp_request *req; + if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL) + return (-1); + + req->evcon = evcon; /* the request ends up owning the connection */ + req->flags |= EVHTTP_REQ_OWN_CONNECTION; + + TAILQ_INSERT_TAIL(&evcon->requests, req, next); + + req->kind = EVHTTP_REQUEST; + + if ((req->remote_host = strdup(evcon->address)) == NULL) + event_err(1, "%s: strdup", __func__); + req->remote_port = evcon->port; + + evhttp_start_read(evcon); + + return (0); +} + +void +evhttp_get_request(struct evhttp *http, int fd, + struct sockaddr *sa, socklen_t salen) +{ + struct evhttp_connection *evcon; + + evcon = evhttp_get_request_connection(http, fd, sa, salen); + if (evcon == NULL) + return; + + /* the timeout can be used by the server to close idle connections */ + if (http->timeout != -1) + evhttp_connection_set_timeout(evcon, http->timeout); + + /* + * if we want to accept more than one request on a connection, + * we need to know which http server it belongs to. + */ + evcon->http_server = http; + TAILQ_INSERT_TAIL(&http->connections, evcon, next); + + if (evhttp_associate_new_request_with_connection(evcon) == -1) + evhttp_connection_free(evcon); +} + + +/* + * Network helper functions that we do not want to export to the rest of + * the world. + */ +#if 0 /* Unused */ +static struct addrinfo * +addr_from_name(char *address) +{ +#ifdef HAVE_GETADDRINFO + struct addrinfo ai, *aitop; + int ai_result; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_RAW; + ai.ai_flags = 0; + if ((ai_result = getaddrinfo(address, NULL, &ai, &aitop)) != 0) { + if ( ai_result == EAI_SYSTEM ) + event_warn("getaddrinfo"); + else + event_warnx("getaddrinfo: %s", gai_strerror(ai_result)); + } + + return (aitop); +#else + assert(0); + return NULL; /* XXXXX Use gethostbyname, if this function is ever used. */ +#endif +} +#endif + +static void +name_from_addr(struct sockaddr *sa, socklen_t salen, + char **phost, char **pport) +{ + char ntop[NI_MAXHOST]; + char strport[NI_MAXSERV]; + int ni_result; + +#ifdef HAVE_GETNAMEINFO + ni_result = getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV); + + if (ni_result != 0) { + if (ni_result == EAI_SYSTEM) + event_err(1, "getnameinfo failed"); + else + event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); + return; + } +#else + ni_result = fake_getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV); + if (ni_result != 0) + return; +#endif + *phost = strdup(ntop); + *pport = strdup(strport); +} + +/* Create a non-blocking socket and bind it */ +/* todo: rename this function */ +static int +bind_socket_ai(struct addrinfo *ai, int reuse) +{ + int fd, on = 1, r; + int serrno; + + /* Create listen socket */ + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + event_warn("socket"); + return (-1); + } + + if (evutil_make_socket_nonblocking(fd) < 0) + goto out; + +#ifndef WIN32 + if (fcntl(fd, F_SETFD, 1) == -1) { + event_warn("fcntl(F_SETFD)"); + goto out; + } +#endif + + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); + if (reuse) { + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (void *)&on, sizeof(on)); + } + + if (ai != NULL) { + r = bind(fd, ai->ai_addr, ai->ai_addrlen); + if (r == -1) + goto out; + } + + return (fd); + + out: + serrno = EVUTIL_SOCKET_ERROR(); + EVUTIL_CLOSESOCKET(fd); + EVUTIL_SET_SOCKET_ERROR(serrno); + return (-1); +} + +static struct addrinfo * +make_addrinfo(const char *address, u_short port) +{ + struct addrinfo *aitop = NULL; + +#ifdef HAVE_GETADDRINFO + struct addrinfo ai; + char strport[NI_MAXSERV]; + int ai_result; + + memset(&ai, 0, sizeof(ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_STREAM; + ai.ai_flags = AI_PASSIVE; /* turn NULL host name into INADDR_ANY */ + evutil_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 (NULL); + } +#else + 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 (NULL); + } + aitop = &ai[cur]; + ((struct sockaddr_in *) aitop->ai_addr)->sin_port = htons(port); +#endif + + return (aitop); +} + +static int +bind_socket(const char *address, u_short port, int reuse) +{ + int fd; + struct addrinfo *aitop = NULL; + + /* just create an unbound socket */ + if (address == NULL && port == 0) + return bind_socket_ai(NULL, 0); + + aitop = make_addrinfo(address, port); + + if (aitop == NULL) + return (-1); + + fd = bind_socket_ai(aitop, reuse); + +#ifdef HAVE_GETADDRINFO + freeaddrinfo(aitop); +#else + fake_freeaddrinfo(aitop); +#endif + + 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); +} diff --git a/libevent/kqueue.c b/libevent/kqueue.c new file mode 100644 index 00000000000..36eebe5fc6e --- /dev/null +++ b/libevent/kqueue.c @@ -0,0 +1,449 @@ +/* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 + +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* Some platforms apparently define the udata field of struct kevent as + * 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)) +#else +#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 + +#define NEVENT 64 + +struct kqop { + struct kevent *changes; + int nchanges; + struct kevent *events; + struct event_list evsigevents[NSIG]; + int nevents; + int kq; + pid_t pid; +}; + +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_dispatch, + kq_dealloc, + 1 /* need reinit */ +}; + +static void * +kq_init(struct event_base *base) +{ + int i, kq; + struct kqop *kqueueop; + + /* Disable kqueue when this environment variable is set */ + if (getenv("EVENT_NOKQUEUE")) + return (NULL); + + if (!(kqueueop = calloc(1, sizeof(struct kqop)))) + return (NULL); + + /* Initalize the kernel queue */ + + if ((kq = kqueue()) == -1) { + event_warn("kqueue"); + free (kqueueop); + return (NULL); + } + + kqueueop->kq = kq; + + kqueueop->pid = getpid(); + + /* Initalize fields */ + kqueueop->changes = malloc(NEVENT * sizeof(struct kevent)); + if (kqueueop->changes == NULL) { + free (kqueueop); + return (NULL); + } + kqueueop->events = malloc(NEVENT * sizeof(struct kevent)); + if (kqueueop->events == NULL) { + free (kqueueop->changes); + free (kqueueop); + return (NULL); + } + kqueueop->nevents = NEVENT; + + /* we need to keep track of multiple events per signal */ + for (i = 0; i < NSIG; ++i) { + TAILQ_INIT(&kqueueop->evsigevents[i]); + } + + /* Check for Mac OS X kqueue bug. */ + kqueueop->changes[0].ident = -1; + kqueueop->changes[0].filter = EVFILT_READ; + kqueueop->changes[0].flags = EV_ADD; + /* + * If kqueue works, then kevent will succeed, and it will + * stick an error in events[0]. If kqueue is broken, then + * kevent will fail. + */ + if (kevent(kq, + kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 || + kqueueop->events[0].ident != -1 || + kqueueop->events[0].flags != EV_ERROR) { + event_warn("%s: detected broken kqueue; not using.", __func__); + free(kqueueop->changes); + free(kqueueop->events); + free(kqueueop); + close(kq); + return (NULL); + } + + return (kqueueop); +} + +static int +kq_insert(struct kqop *kqop, struct kevent *kev) +{ + int nevents = kqop->nevents; + + if (kqop->nchanges == nevents) { + struct kevent *newchange; + struct kevent *newresult; + + nevents *= 2; + + newchange = realloc(kqop->changes, + nevents * sizeof(struct kevent)); + if (newchange == NULL) { + event_warn("%s: malloc", __func__); + return (-1); + } + kqop->changes = newchange; + + newresult = realloc(kqop->events, + nevents * sizeof(struct kevent)); + + /* + * If we fail, we don't have to worry about freeing, + * the next realloc will pick it up. + */ + if (newresult == NULL) { + event_warn("%s: malloc", __func__); + return (-1); + } + kqop->events = newresult; + + kqop->nevents = nevents; + } + + memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent)); + + event_debug(("%s: fd %d %s%s", + __func__, (int)kev->ident, + kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", + kev->flags == EV_DELETE ? " (del)" : "")); + + return (0); +} + +static void +kq_sighandler(int sig) +{ + /* Do nothing here */ +} + +static int +kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + struct kqop *kqop = arg; + struct kevent *changes = kqop->changes; + struct kevent *events = kqop->events; + struct event *ev; + struct timespec ts, *ts_p = NULL; + int i, res; + + if (tv != NULL) { + TIMEVAL_TO_TIMESPEC(tv, &ts); + ts_p = &ts; + } + + res = kevent(kqop->kq, changes, kqop->nchanges, + events, kqop->nevents, ts_p); + kqop->nchanges = 0; + if (res == -1) { + if (errno != EINTR) { + event_warn("kevent"); + return (-1); + } + + return (0); + } + + event_debug(("%s: kevent reports %d", __func__, res)); + + for (i = 0; i < res; i++) { + int which = 0; + + if (events[i].flags & EV_ERROR) { + /* + * Error messages that can happen, when a delete fails. + * EBADF happens when the file discriptor has been + * closed, + * ENOENT when the file discriptor was closed and + * then reopened. + * EINVAL for some reasons not understood; EINVAL + * should not be returned ever; but FreeBSD does :-\ + * An error is also indicated when a callback deletes + * an event we are still processing. In that case + * the data field is set to ENOENT. + */ + if (events[i].data == EBADF || + events[i].data == EINVAL || + events[i].data == ENOENT) + continue; + errno = events[i].data; + return (-1); + } + + if (events[i].filter == EVFILT_READ) { + which |= EV_READ; + } else if (events[i].filter == EVFILT_WRITE) { + which |= EV_WRITE; + } else if (events[i].filter == EVFILT_SIGNAL) { + which |= EV_SIGNAL; + } + + if (!which) + continue; + + if (events[i].filter == EVFILT_SIGNAL) { + struct event_list *head = + (struct event_list *)events[i].udata; + TAILQ_FOREACH(ev, head, ev_signal_next) { + event_active(ev, which, events[i].data); + } + } else { + ev = (struct event *)events[i].udata; + + if (!(ev->ev_events & EV_PERSIST)) + ev->ev_flags &= ~EVLIST_X_KQINKERNEL; + + event_active(ev, which, 1); + } + } + + return (0); +} + + +static int +kq_add(void *arg, struct event *ev) +{ + struct kqop *kqop = arg; + struct kevent kev; + + if (ev->ev_events & EV_SIGNAL) { + int nsignal = EVENT_SIGNAL(ev); + + assert(nsignal >= 0 && nsignal < NSIG); + if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { + struct timespec timeout = { 0, 0 }; + + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_ADD; + kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]); + + /* 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); + } + + TAILQ_INSERT_TAIL(&kqop->evsigevents[nsignal], ev, + ev_signal_next); + ev->ev_flags |= EVLIST_X_KQINKERNEL; + return (0); + } + + if (ev->ev_events & EV_READ) { + memset(&kev, 0, sizeof(kev)); + kev.ident = ev->ev_fd; + kev.filter = EVFILT_READ; +#ifdef NOTE_EOF + /* Make it behave like select() and poll() */ + kev.fflags = NOTE_EOF; +#endif + kev.flags = EV_ADD; + if (!(ev->ev_events & EV_PERSIST)) + kev.flags |= EV_ONESHOT; + kev.udata = PTR_TO_UDATA(ev); + + if (kq_insert(kqop, &kev) == -1) + return (-1); + + ev->ev_flags |= EVLIST_X_KQINKERNEL; + } + + if (ev->ev_events & EV_WRITE) { + memset(&kev, 0, sizeof(kev)); + kev.ident = ev->ev_fd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_ADD; + if (!(ev->ev_events & EV_PERSIST)) + kev.flags |= EV_ONESHOT; + kev.udata = PTR_TO_UDATA(ev); + + if (kq_insert(kqop, &kev) == -1) + return (-1); + + ev->ev_flags |= EVLIST_X_KQINKERNEL; + } + + return (0); +} + +static int +kq_del(void *arg, struct event *ev) +{ + struct kqop *kqop = arg; + struct kevent kev; + + if (!(ev->ev_flags & EVLIST_X_KQINKERNEL)) + return (0); + + if (ev->ev_events & EV_SIGNAL) { + int nsignal = EVENT_SIGNAL(ev); + struct timespec timeout = { 0, 0 }; + + assert(nsignal >= 0 && nsignal < NSIG); + TAILQ_REMOVE(&kqop->evsigevents[nsignal], ev, ev_signal_next); + if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { + memset(&kev, 0, sizeof(kev)); + kev.ident = nsignal; + kev.filter = EVFILT_SIGNAL; + kev.flags = EV_DELETE; + + /* Because we insert signal events + * immediately, we need to delete them + * immediately, too */ + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) + return (-1); + + if (_evsignal_restore_handler(ev->ev_base, + nsignal) == -1) + return (-1); + } + + ev->ev_flags &= ~EVLIST_X_KQINKERNEL; + return (0); + } + + if (ev->ev_events & EV_READ) { + memset(&kev, 0, sizeof(kev)); + kev.ident = ev->ev_fd; + kev.filter = EVFILT_READ; + kev.flags = EV_DELETE; + + if (kq_insert(kqop, &kev) == -1) + return (-1); + + ev->ev_flags &= ~EVLIST_X_KQINKERNEL; + } + + if (ev->ev_events & EV_WRITE) { + memset(&kev, 0, sizeof(kev)); + kev.ident = ev->ev_fd; + kev.filter = EVFILT_WRITE; + kev.flags = EV_DELETE; + + if (kq_insert(kqop, &kev) == -1) + return (-1); + + ev->ev_flags &= ~EVLIST_X_KQINKERNEL; + } + + return (0); +} + +static void +kq_dealloc(struct event_base *base, void *arg) +{ + struct kqop *kqop = arg; + + if (kqop->changes) + free(kqop->changes); + if (kqop->events) + free(kqop->events); + if (kqop->kq >= 0 && kqop->pid == getpid()) + close(kqop->kq); + memset(kqop, 0, sizeof(struct kqop)); + free(kqop); +} diff --git a/libevent/log.c b/libevent/log.c new file mode 100644 index 00000000000..b62a61915d1 --- /dev/null +++ b/libevent/log.c @@ -0,0 +1,187 @@ +/* $OpenBSD: err.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * log.c + * + * Based on err.c, which was adapted from OpenBSD libc *err* *warn* code. + * + * Copyright (c) 2005 Nick Mathewson + * + * Copyright (c) 2000 Dug Song + * + * Copyright (c) 1993 + * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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 +#undef WIN32_LEAN_AND_MEAN +#endif +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include "event.h" + +#include "log.h" +#include "evutil.h" + +static void _warn_helper(int severity, int log_errno, const char *fmt, + va_list ap); +static void event_log(int severity, const char *msg); + +void +event_err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_ERR, errno, fmt, ap); + va_end(ap); + exit(eval); +} + +void +event_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_WARN, errno, fmt, ap); + va_end(ap); +} + +void +event_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_ERR, -1, fmt, ap); + va_end(ap); + exit(eval); +} + +void +event_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_WARN, -1, fmt, ap); + va_end(ap); +} + +void +event_msgx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_MSG, -1, fmt, ap); + va_end(ap); +} + +void +_event_debugx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _warn_helper(_EVENT_LOG_DEBUG, -1, fmt, ap); + va_end(ap); +} + +static void +_warn_helper(int severity, int log_errno, const char *fmt, va_list ap) +{ + char buf[1024]; + size_t len; + + if (fmt != NULL) + evutil_vsnprintf(buf, sizeof(buf), fmt, ap); + else + buf[0] = '\0'; + + if (log_errno >= 0) { + len = strlen(buf); + if (len < sizeof(buf) - 3) { + evutil_snprintf(buf + len, sizeof(buf) - len, ": %s", + strerror(log_errno)); + } + } + + event_log(severity, buf); +} + +static event_log_cb log_fn = NULL; + +void +event_set_log_callback(event_log_cb cb) +{ + log_fn = cb; +} + +static void +event_log(int severity, const char *msg) +{ + if (log_fn) + log_fn(severity, msg); + else { + const char *severity_str; + switch (severity) { + case _EVENT_LOG_DEBUG: + severity_str = "debug"; + break; + case _EVENT_LOG_MSG: + severity_str = "msg"; + break; + case _EVENT_LOG_WARN: + severity_str = "warn"; + break; + case _EVENT_LOG_ERR: + severity_str = "err"; + break; + default: + severity_str = "???"; + break; + } + (void)fprintf(stderr, "[%s] %s\n", severity_str, msg); + } +} diff --git a/libevent/log.h b/libevent/log.h new file mode 100644 index 00000000000..7bc6632b8dd --- /dev/null +++ b/libevent/log.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 _LOG_H_ +#define _LOG_H_ + +#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 +#else +#define event_debug(x) do {;} while (0) +#endif + +#undef EV_CHECK_FMT + +#endif diff --git a/libevent/min_heap.h b/libevent/min_heap.h new file mode 100644 index 00000000000..edaa5ae1270 --- /dev/null +++ b/libevent/min_heap.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2006 Maxim Yegorushkin + * 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" +#include "evutil.h" +#include "stdlib.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 evutil_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; + min_heap_shift_down_(s, 0u, s->p[--s->n]); + e->min_heap_idx = -1; + return e; + } + return 0; +} + +int min_heap_erase(min_heap_t* s, struct event* e) +{ + if(((unsigned int)-1) != e->min_heap_idx) + { + struct event *last = s->p[--s->n]; + unsigned parent = (e->min_heap_idx - 1) / 2; + /* we replace e with the last element in the heap. We might need to + shift it upward if it is less than its parent, or downward if it is + greater than one or both its children. Since the children are known + to be less than the parent, it can't need to shift both up and + down. */ + if (e->min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last)) + min_heap_shift_up_(s, e->min_heap_idx, last); + else + min_heap_shift_down_(s, e->min_heap_idx, last); + 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_ */ diff --git a/libevent/poll.c b/libevent/poll.c new file mode 100644 index 00000000000..5d496618d29 --- /dev/null +++ b/libevent/poll.c @@ -0,0 +1,379 @@ +/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2003 Niels Provos + * 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 + +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CHECK_INVARIANTS +#include +#endif + +#include "event.h" +#include "event-internal.h" +#include "evsignal.h" +#include "log.h" + +struct pollop { + int event_count; /* Highest number alloc */ + int nfds; /* Size of event_* */ + int fd_count; /* Size of idxplus1_by_fd */ + struct pollfd *event_set; + struct event **event_r_back; + struct event **event_w_back; + int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so + * that 0 (which is easy to memset) can mean + * "no entry." */ +}; + +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_dispatch, + poll_dealloc, + 0 +}; + +static void * +poll_init(struct event_base *base) +{ + struct pollop *pollop; + + /* Disable poll when this environment variable is set */ + if (getenv("EVENT_NOPOLL")) + return (NULL); + + if (!(pollop = calloc(1, sizeof(struct pollop)))) + return (NULL); + + evsignal_init(base); + + return (pollop); +} + +#ifdef CHECK_INVARIANTS +static void +poll_check_ok(struct pollop *pop) +{ + int i, idx; + struct event *ev; + + for (i = 0; i < pop->fd_count; ++i) { + idx = pop->idxplus1_by_fd[i]-1; + if (idx < 0) + continue; + assert(pop->event_set[idx].fd == i); + if (pop->event_set[idx].events & POLLIN) { + ev = pop->event_r_back[idx]; + assert(ev); + assert(ev->ev_events & EV_READ); + assert(ev->ev_fd == i); + } + if (pop->event_set[idx].events & POLLOUT) { + ev = pop->event_w_back[idx]; + assert(ev); + assert(ev->ev_events & EV_WRITE); + assert(ev->ev_fd == i); + } + } + for (i = 0; i < pop->nfds; ++i) { + struct pollfd *pfd = &pop->event_set[i]; + assert(pop->idxplus1_by_fd[pfd->fd] == i+1); + } +} +#else +#define poll_check_ok(pop) +#endif + +static int +poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + int res, i, j, msec = -1, nfds; + struct pollop *pop = arg; + + poll_check_ok(pop); + + if (tv != NULL) + msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + + nfds = pop->nfds; + res = poll(pop->event_set, nfds, msec); + + if (res == -1) { + if (errno != EINTR) { + event_warn("poll"); + return (-1); + } + + evsignal_process(base); + return (0); + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + event_debug(("%s: poll reports %d", __func__, res)); + + if (res == 0 || nfds == 0) + return (0); + + i = random() % nfds; + for (j = 0; j < nfds; j++) { + struct event *r_ev = NULL, *w_ev = NULL; + int what; + if (++i == nfds) + i = 0; + what = pop->event_set[i].revents; + + if (!what) + continue; + + res = 0; + + /* If the file gets closed notify */ + if (what & (POLLHUP|POLLERR)) + what |= POLLIN|POLLOUT; + if (what & POLLIN) { + res |= EV_READ; + r_ev = pop->event_r_back[i]; + } + if (what & POLLOUT) { + res |= EV_WRITE; + w_ev = pop->event_w_back[i]; + } + if (res == 0) + continue; + + if (r_ev && (res & r_ev->ev_events)) { + event_active(r_ev, res & r_ev->ev_events, 1); + } + if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { + event_active(w_ev, res & w_ev->ev_events, 1); + } + } + + return (0); +} + +static int +poll_add(void *arg, struct event *ev) +{ + struct pollop *pop = arg; + struct pollfd *pfd = NULL; + int i; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(ev)); + if (!(ev->ev_events & (EV_READ|EV_WRITE))) + return (0); + + poll_check_ok(pop); + if (pop->nfds + 1 >= pop->event_count) { + struct pollfd *tmp_event_set; + struct event **tmp_event_r_back; + struct event **tmp_event_w_back; + int tmp_event_count; + + if (pop->event_count < 32) + tmp_event_count = 32; + else + tmp_event_count = pop->event_count * 2; + + /* We need more file descriptors */ + tmp_event_set = realloc(pop->event_set, + tmp_event_count * sizeof(struct pollfd)); + if (tmp_event_set == NULL) { + event_warn("realloc"); + return (-1); + } + pop->event_set = tmp_event_set; + + tmp_event_r_back = realloc(pop->event_r_back, + tmp_event_count * sizeof(struct event *)); + if (tmp_event_r_back == NULL) { + /* event_set overallocated; that's okay. */ + event_warn("realloc"); + return (-1); + } + pop->event_r_back = tmp_event_r_back; + + tmp_event_w_back = realloc(pop->event_w_back, + tmp_event_count * sizeof(struct event *)); + if (tmp_event_w_back == NULL) { + /* event_set and event_r_back overallocated; that's + * okay. */ + event_warn("realloc"); + return (-1); + } + pop->event_w_back = tmp_event_w_back; + + pop->event_count = tmp_event_count; + } + if (ev->ev_fd >= pop->fd_count) { + int *tmp_idxplus1_by_fd; + int new_count; + if (pop->fd_count < 32) + new_count = 32; + else + new_count = pop->fd_count * 2; + while (new_count <= ev->ev_fd) + new_count *= 2; + tmp_idxplus1_by_fd = + realloc(pop->idxplus1_by_fd, new_count * sizeof(int)); + if (tmp_idxplus1_by_fd == NULL) { + event_warn("realloc"); + return (-1); + } + pop->idxplus1_by_fd = tmp_idxplus1_by_fd; + memset(pop->idxplus1_by_fd + pop->fd_count, + 0, sizeof(int)*(new_count - pop->fd_count)); + pop->fd_count = new_count; + } + + i = pop->idxplus1_by_fd[ev->ev_fd] - 1; + if (i >= 0) { + pfd = &pop->event_set[i]; + } else { + i = pop->nfds++; + pfd = &pop->event_set[i]; + pfd->events = 0; + pfd->fd = ev->ev_fd; + pop->event_w_back[i] = pop->event_r_back[i] = NULL; + pop->idxplus1_by_fd[ev->ev_fd] = i + 1; + } + + pfd->revents = 0; + if (ev->ev_events & EV_WRITE) { + pfd->events |= POLLOUT; + pop->event_w_back[i] = ev; + } + if (ev->ev_events & EV_READ) { + pfd->events |= POLLIN; + pop->event_r_back[i] = ev; + } + poll_check_ok(pop); + + return (0); +} + +/* + * Nothing to be done here. + */ + +static int +poll_del(void *arg, struct event *ev) +{ + struct pollop *pop = arg; + struct pollfd *pfd = NULL; + int i; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(ev)); + + if (!(ev->ev_events & (EV_READ|EV_WRITE))) + return (0); + + poll_check_ok(pop); + i = pop->idxplus1_by_fd[ev->ev_fd] - 1; + if (i < 0) + return (-1); + + /* Do we still want to read or write? */ + pfd = &pop->event_set[i]; + if (ev->ev_events & EV_READ) { + pfd->events &= ~POLLIN; + pop->event_r_back[i] = NULL; + } + if (ev->ev_events & EV_WRITE) { + pfd->events &= ~POLLOUT; + pop->event_w_back[i] = NULL; + } + poll_check_ok(pop); + if (pfd->events) + /* Another event cares about that fd. */ + return (0); + + /* Okay, so we aren't interested in that fd anymore. */ + pop->idxplus1_by_fd[ev->ev_fd] = 0; + + --pop->nfds; + if (i != pop->nfds) { + /* + * Shift the last pollfd down into the now-unoccupied + * position. + */ + memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], + sizeof(struct pollfd)); + pop->event_r_back[i] = pop->event_r_back[pop->nfds]; + pop->event_w_back[i] = pop->event_w_back[pop->nfds]; + pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1; + } + + poll_check_ok(pop); + return (0); +} + +static void +poll_dealloc(struct event_base *base, void *arg) +{ + struct pollop *pop = arg; + + evsignal_dealloc(base); + if (pop->event_set) + free(pop->event_set); + if (pop->event_r_back) + free(pop->event_r_back); + if (pop->event_w_back) + free(pop->event_w_back); + if (pop->idxplus1_by_fd) + free(pop->idxplus1_by_fd); + + memset(pop, 0, sizeof(struct pollop)); + free(pop); +} diff --git a/libevent/sample/Makefile.am b/libevent/sample/Makefile.am new file mode 100644 index 00000000000..2f4e26e2f3f --- /dev/null +++ b/libevent/sample/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +LDADD = ../libevent.la +AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat + +noinst_PROGRAMS = event-test time-test signal-test + +event_test_sources = event-test.c +time_test_sources = time-test.c +signal_test_sources = signal-test.c + +verify: + +DISTCLEANFILES = *~ diff --git a/libevent/sample/event-test.c b/libevent/sample/event-test.c new file mode 100644 index 00000000000..2c6cb93864c --- /dev/null +++ b/libevent/sample/event-test.c @@ -0,0 +1,139 @@ +/* + * Compile with: + * cc -I/usr/local/include -o event-test event-test.c -L/usr/local/lib -levent + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifndef WIN32 +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include + +static void +fifo_read(int fd, short event, void *arg) +{ + char buf[255]; + int len; + struct event *ev = arg; +#ifdef WIN32 + DWORD dwBytesRead; +#endif + + /* Reschedule this event */ + event_add(ev, NULL); + + fprintf(stderr, "fifo_read called with fd: %d, event: %d, arg: %p\n", + fd, event, arg); +#ifdef WIN32 + len = ReadFile((HANDLE)fd, buf, sizeof(buf) - 1, &dwBytesRead, NULL); + + // Check for end of file. + if(len && dwBytesRead == 0) { + fprintf(stderr, "End Of File"); + event_del(ev); + return; + } + + buf[dwBytesRead] = '\0'; +#else + len = read(fd, buf, sizeof(buf) - 1); + + if (len == -1) { + perror("read"); + return; + } else if (len == 0) { + fprintf(stderr, "Connection closed\n"); + return; + } + + buf[len] = '\0'; +#endif + fprintf(stdout, "Read: %s\n", buf); +} + +int +main (int argc, char **argv) +{ + struct event evfifo; +#ifdef WIN32 + HANDLE socket; + // Open a file. + socket = CreateFile("test.txt", // open File + GENERIC_READ, // open for reading + 0, // do not share + NULL, // no security + OPEN_EXISTING, // existing file only + FILE_ATTRIBUTE_NORMAL, // normal file + NULL); // no attr. template + + if(socket == INVALID_HANDLE_VALUE) + return 1; + +#else + struct stat st; + const char *fifo = "event.fifo"; + int socket; + + if (lstat (fifo, &st) == 0) { + if ((st.st_mode & S_IFMT) == S_IFREG) { + errno = EEXIST; + perror("lstat"); + exit (1); + } + } + + unlink (fifo); + if (mkfifo (fifo, 0600) == -1) { + perror("mkfifo"); + exit (1); + } + + /* Linux pipes are broken, we need O_RDWR instead of O_RDONLY */ +#ifdef __linux + socket = open (fifo, O_RDWR | O_NONBLOCK, 0); +#else + socket = open (fifo, O_RDONLY | O_NONBLOCK, 0); +#endif + + if (socket == -1) { + perror("open"); + exit (1); + } + + fprintf(stderr, "Write data to %s\n", fifo); +#endif + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ +#ifdef WIN32 + event_set(&evfifo, (int)socket, EV_READ, fifo_read, &evfifo); +#else + event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); +#endif + + /* Add it to the active events, without a timeout */ + event_add(&evfifo, NULL); + + event_dispatch(); +#ifdef WIN32 + CloseHandle(socket); +#endif + return (0); +} + diff --git a/libevent/sample/signal-test.c b/libevent/sample/signal-test.c new file mode 100644 index 00000000000..9a131cb50c2 --- /dev/null +++ b/libevent/sample/signal-test.c @@ -0,0 +1,63 @@ +/* + * Compile with: + * cc -I/usr/local/include -o signal-test \ + * signal-test.c -L/usr/local/lib -levent + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifndef WIN32 +#include +#include +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +int called = 0; + +static void +signal_cb(int fd, short event, void *arg) +{ + struct event *signal = arg; + + printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal)); + + if (called >= 2) + event_del(signal); + + called++; +} + +int +main (int argc, char **argv) +{ + struct event signal_int; + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, + &signal_int); + + event_add(&signal_int, NULL); + + event_dispatch(); + + return (0); +} + diff --git a/libevent/sample/time-test.c b/libevent/sample/time-test.c new file mode 100644 index 00000000000..069d4f8f783 --- /dev/null +++ b/libevent/sample/time-test.c @@ -0,0 +1,70 @@ +/* + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#ifndef WIN32 +#include +#include +#endif +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +#include +#include +#include + +#include +#include + +int lasttime; + +static void +timeout_cb(int fd, short event, void *arg) +{ + struct timeval tv; + struct event *timeout = arg; + int newtime = time(NULL); + + printf("%s: called at %d: %d\n", __func__, newtime, + newtime - lasttime); + lasttime = newtime; + + evutil_timerclear(&tv); + tv.tv_sec = 2; + event_add(timeout, &tv); +} + +int +main (int argc, char **argv) +{ + struct event timeout; + struct timeval tv; + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + evtimer_set(&timeout, timeout_cb, &timeout); + + evutil_timerclear(&tv); + tv.tv_sec = 2; + event_add(&timeout, &tv); + + lasttime = time(NULL); + + event_dispatch(); + + return (0); +} + diff --git a/libevent/select.c b/libevent/select.c new file mode 100644 index 00000000000..ca6639fd829 --- /dev/null +++ b/libevent/select.c @@ -0,0 +1,356 @@ +/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 + +#include +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef CHECK_INVARIANTS +#include +#endif + +#include "event.h" +#include "event-internal.h" +#include "evsignal.h" +#include "log.h" + +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif + +struct selectop { + int event_fds; /* Highest fd in fd set */ + int event_fdsz; + fd_set *event_readset_in; + fd_set *event_writeset_in; + fd_set *event_readset_out; + fd_set *event_writeset_out; + struct event **event_r_by_fd; + struct event **event_w_by_fd; +}; + +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_dispatch, + select_dealloc, + 0 +}; + +static int select_resize(struct selectop *sop, int fdsz); + +static void * +select_init(struct event_base *base) +{ + struct selectop *sop; + + /* Disable select when this environment variable is set */ + if (getenv("EVENT_NOSELECT")) + return (NULL); + + if (!(sop = calloc(1, sizeof(struct selectop)))) + return (NULL); + + select_resize(sop, howmany(32 + 1, NFDBITS)*sizeof(fd_mask)); + + evsignal_init(base); + + return (sop); +} + +#ifdef CHECK_INVARIANTS +static void +check_selectop(struct selectop *sop) +{ + int i; + for (i = 0; i <= sop->event_fds; ++i) { + if (FD_ISSET(i, sop->event_readset_in)) { + assert(sop->event_r_by_fd[i]); + assert(sop->event_r_by_fd[i]->ev_events & EV_READ); + assert(sop->event_r_by_fd[i]->ev_fd == i); + } else { + assert(! sop->event_r_by_fd[i]); + } + if (FD_ISSET(i, sop->event_writeset_in)) { + assert(sop->event_w_by_fd[i]); + assert(sop->event_w_by_fd[i]->ev_events & EV_WRITE); + assert(sop->event_w_by_fd[i]->ev_fd == i); + } else { + assert(! sop->event_w_by_fd[i]); + } + } + +} +#else +#define check_selectop(sop) do { (void) sop; } while (0) +#endif + +static int +select_dispatch(struct event_base *base, void *arg, struct timeval *tv) +{ + int res, i, j; + struct selectop *sop = arg; + + check_selectop(sop); + + memcpy(sop->event_readset_out, sop->event_readset_in, + sop->event_fdsz); + memcpy(sop->event_writeset_out, sop->event_writeset_in, + sop->event_fdsz); + + res = select(sop->event_fds + 1, sop->event_readset_out, + sop->event_writeset_out, NULL, tv); + + check_selectop(sop); + + if (res == -1) { + if (errno != EINTR) { + event_warn("select"); + return (-1); + } + + evsignal_process(base); + return (0); + } else if (base->sig.evsignal_caught) { + evsignal_process(base); + } + + event_debug(("%s: select reports %d", __func__, res)); + + check_selectop(sop); + i = random() % (sop->event_fds+1); + for (j = 0; j <= sop->event_fds; ++j) { + struct event *r_ev = NULL, *w_ev = NULL; + if (++i >= sop->event_fds+1) + i = 0; + + res = 0; + if (FD_ISSET(i, sop->event_readset_out)) { + r_ev = sop->event_r_by_fd[i]; + res |= EV_READ; + } + if (FD_ISSET(i, sop->event_writeset_out)) { + w_ev = sop->event_w_by_fd[i]; + res |= EV_WRITE; + } + if (r_ev && (res & r_ev->ev_events)) { + event_active(r_ev, res & r_ev->ev_events, 1); + } + if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { + event_active(w_ev, res & w_ev->ev_events, 1); + } + } + check_selectop(sop); + + return (0); +} + + +static int +select_resize(struct selectop *sop, int fdsz) +{ + int n_events, n_events_old; + + fd_set *readset_in = NULL; + fd_set *writeset_in = NULL; + fd_set *readset_out = NULL; + fd_set *writeset_out = NULL; + struct event **r_by_fd = NULL; + struct event **w_by_fd = NULL; + + n_events = (fdsz/sizeof(fd_mask)) * NFDBITS; + n_events_old = (sop->event_fdsz/sizeof(fd_mask)) * NFDBITS; + + if (sop->event_readset_in) + check_selectop(sop); + + if ((readset_in = realloc(sop->event_readset_in, fdsz)) == NULL) + goto error; + sop->event_readset_in = readset_in; + if ((readset_out = realloc(sop->event_readset_out, fdsz)) == NULL) + goto error; + sop->event_readset_out = readset_out; + if ((writeset_in = realloc(sop->event_writeset_in, fdsz)) == NULL) + goto error; + sop->event_writeset_in = writeset_in; + if ((writeset_out = realloc(sop->event_writeset_out, fdsz)) == NULL) + goto error; + sop->event_writeset_out = writeset_out; + if ((r_by_fd = realloc(sop->event_r_by_fd, + n_events*sizeof(struct event*))) == NULL) + goto error; + sop->event_r_by_fd = r_by_fd; + if ((w_by_fd = realloc(sop->event_w_by_fd, + n_events * sizeof(struct event*))) == NULL) + goto error; + sop->event_w_by_fd = w_by_fd; + + memset((char *)sop->event_readset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + memset((char *)sop->event_writeset_in + sop->event_fdsz, 0, + fdsz - sop->event_fdsz); + memset(sop->event_r_by_fd + n_events_old, 0, + (n_events-n_events_old) * sizeof(struct event*)); + memset(sop->event_w_by_fd + n_events_old, 0, + (n_events-n_events_old) * sizeof(struct event*)); + + sop->event_fdsz = fdsz; + check_selectop(sop); + + return (0); + + error: + event_warn("malloc"); + return (-1); +} + + +static int +select_add(void *arg, struct event *ev) +{ + struct selectop *sop = arg; + + if (ev->ev_events & EV_SIGNAL) + return (evsignal_add(ev)); + + check_selectop(sop); + /* + * Keep track of the highest fd, so that we can calculate the size + * of the fd_sets for select(2) + */ + if (sop->event_fds < ev->ev_fd) { + int fdsz = sop->event_fdsz; + + if (fdsz < sizeof(fd_mask)) + fdsz = sizeof(fd_mask); + + while (fdsz < + (howmany(ev->ev_fd + 1, NFDBITS) * sizeof(fd_mask))) + fdsz *= 2; + + if (fdsz != sop->event_fdsz) { + if (select_resize(sop, fdsz)) { + check_selectop(sop); + return (-1); + } + } + + sop->event_fds = ev->ev_fd; + } + + if (ev->ev_events & EV_READ) { + FD_SET(ev->ev_fd, sop->event_readset_in); + sop->event_r_by_fd[ev->ev_fd] = ev; + } + if (ev->ev_events & EV_WRITE) { + FD_SET(ev->ev_fd, sop->event_writeset_in); + sop->event_w_by_fd[ev->ev_fd] = ev; + } + check_selectop(sop); + + return (0); +} + +/* + * Nothing to be done here. + */ + +static int +select_del(void *arg, struct event *ev) +{ + struct selectop *sop = arg; + + check_selectop(sop); + if (ev->ev_events & EV_SIGNAL) + return (evsignal_del(ev)); + + if (sop->event_fds < ev->ev_fd) { + check_selectop(sop); + return (0); + } + + if (ev->ev_events & EV_READ) { + FD_CLR(ev->ev_fd, sop->event_readset_in); + sop->event_r_by_fd[ev->ev_fd] = NULL; + } + + if (ev->ev_events & EV_WRITE) { + FD_CLR(ev->ev_fd, sop->event_writeset_in); + sop->event_w_by_fd[ev->ev_fd] = NULL; + } + + check_selectop(sop); + return (0); +} + +static void +select_dealloc(struct event_base *base, void *arg) +{ + struct selectop *sop = arg; + + evsignal_dealloc(base); + if (sop->event_readset_in) + free(sop->event_readset_in); + if (sop->event_writeset_in) + free(sop->event_writeset_in); + if (sop->event_readset_out) + free(sop->event_readset_out); + if (sop->event_writeset_out) + free(sop->event_writeset_out); + if (sop->event_r_by_fd) + free(sop->event_r_by_fd); + if (sop->event_w_by_fd) + free(sop->event_w_by_fd); + + memset(sop, 0, sizeof(struct selectop)); + free(sop); +} diff --git a/libevent/signal.c b/libevent/signal.c new file mode 100644 index 00000000000..74fa23f688a --- /dev/null +++ b/libevent/signal.c @@ -0,0 +1,357 @@ +/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ + +/* + * Copyright 2000-2002 Niels Provos + * 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 +#include +#undef WIN32_LEAN_AND_MEAN +#endif +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include + +#include "event.h" +#include "event-internal.h" +#include "evsignal.h" +#include "evutil.h" +#include "log.h" + +struct event_base *evsignal_base = NULL; + +static void evsignal_handler(int sig); + +/* Callback for when the signal handler write a byte to our signaling socket */ +static void +evsignal_cb(int fd, short what, void *arg) +{ + static char signals[1]; +#ifdef WIN32 + SSIZE_T n; +#else + ssize_t n; +#endif + + n = recv(fd, signals, sizeof(signals), 0); + if (n == -1) + event_err(1, "%s: read", __func__); +} + +#ifdef HAVE_SETFD +#define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, 1) == -1) \ + event_warn("fcntl(%d, F_SETFD)", x); \ +} while (0) +#else +#define FD_CLOSEONEXEC(x) +#endif + +int +evsignal_init(struct event_base *base) +{ + int i; + + /* + * Our signal handler is going to write to one end of the socket + * pair to wake up our event loop. The event loop then scans for + * signals that got delivered. + */ + if (evutil_socketpair( + AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) { +#ifdef WIN32 + /* Make this nonfatal on win32, where sometimes people + have localhost firewalled. */ + event_warn("%s: socketpair", __func__); +#else + event_err(1, "%s: socketpair", __func__); +#endif + return -1; + } + + 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); + /* initialize the queues for all events */ + for (i = 0; i < NSIG; ++i) + TAILQ_INIT(&base->sig.evsigevents[i]); + + evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]); + + 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; + + return 0; +} + +/* 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) { + int new_max = evsignal + 1; + event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing", + __func__, evsignal, sig->sh_old_max)); + p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old)); + if (p == NULL) { + event_warn("realloc"); + return (-1); + } + + memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old), + 0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old)); + + sig->sh_old_max = new_max; + 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 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); + assert(evsignal >= 0 && evsignal < NSIG); + if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) { + 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 (!sig->ev_signal_added) { + if (event_add(&sig->ev_signal, NULL)) + return (-1); + sig->ev_signal_added = 1; + } + } + + /* multiple events may listen to the same signal */ + TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next); + + 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) +{ + struct event_base *base = ev->ev_base; + struct evsignal_info *sig = &base->sig; + int evsignal = EVENT_SIGNAL(ev); + + assert(evsignal >= 0 && evsignal < NSIG); + + /* multiple events may listen to the same signal */ + TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next); + + if (!TAILQ_EMPTY(&sig->evsigevents[evsignal])) + return (0); + + event_debug(("%s: %p: restoring signal handler", __func__, ev)); + + return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev))); +} + +static void +evsignal_handler(int sig) +{ + int save_errno = errno; + + if (evsignal_base == NULL) { + event_warn( + "%s: received signal %d, but have no base configured", + __func__, sig); + return; + } + + evsignal_base->sig.evsigcaught[sig]++; + evsignal_base->sig.evsignal_caught = 1; + +#ifndef HAVE_SIGACTION + signal(sig, evsignal_handler); +#endif + + /* Wake up our notification mechanism */ + send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); + errno = save_errno; +} + +void +evsignal_process(struct event_base *base) +{ + struct evsignal_info *sig = &base->sig; + struct event *ev, *next_ev; + sig_atomic_t ncalls; + int i; + + base->sig.evsignal_caught = 0; + for (i = 1; i < NSIG; ++i) { + ncalls = sig->evsigcaught[i]; + if (ncalls == 0) + continue; + sig->evsigcaught[i] -= ncalls; + + for (ev = TAILQ_FIRST(&sig->evsigevents[i]); + ev != NULL; ev = next_ev) { + next_ev = TAILQ_NEXT(ev, ev_signal_next); + if (!(ev->ev_events & EV_PERSIST)) + event_del(ev); + event_active(ev, EV_SIGNAL, ncalls); + } + + } +} + +void +evsignal_dealloc(struct event_base *base) +{ + int i = 0; + if (base->sig.ev_signal_added) { + event_del(&base->sig.ev_signal); + base->sig.ev_signal_added = 0; + } + for (i = 0; i < NSIG; ++i) { + if (i < base->sig.sh_old_max && base->sig.sh_old[i] != NULL) + _evsignal_restore_handler(base, i); + } + + EVUTIL_CLOSESOCKET(base->sig.ev_signal_pair[0]); + base->sig.ev_signal_pair[0] = -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); +} diff --git a/libevent/strlcpy-internal.h b/libevent/strlcpy-internal.h new file mode 100644 index 00000000000..22b5f61d45e --- /dev/null +++ b/libevent/strlcpy-internal.h @@ -0,0 +1,23 @@ +#ifndef _STRLCPY_INTERNAL_H_ +#define _STRLCPY_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef HAVE_STRLCPY +#include +size_t _event_strlcpy(char *dst, const char *src, size_t siz); +#define strlcpy _event_strlcpy +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libevent/strlcpy.c b/libevent/strlcpy.c new file mode 100644 index 00000000000..5d194527c8c --- /dev/null +++ b/libevent/strlcpy.c @@ -0,0 +1,76 @@ +/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * 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 ``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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef HAVE_STRLCPY +#include "strlcpy-internal.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +_event_strlcpy(dst, src, siz) + char *dst; + const char *src; + size_t siz; +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif diff --git a/libevent/test/Makefile.am b/libevent/test/Makefile.am new file mode 100644 index 00000000000..3558d02fd5a --- /dev/null +++ b/libevent/test/Makefile.am @@ -0,0 +1,35 @@ +AUTOMAKE_OPTIONS = foreign no-dependencies + +AM_CFLAGS = -I$(top_srcdir) -I$(top_srcdir)/compat + +EXTRA_DIST = regress.rpc regress.gen.h regress.gen.c + +noinst_PROGRAMS = test-init test-eof test-weof test-time regress bench + +BUILT_SOURCES = regress.gen.c regress.gen.h +test_init_SOURCES = test-init.c +test_init_LDADD = ../libevent_core.la +test_eof_SOURCES = test-eof.c +test_eof_LDADD = ../libevent_core.la +test_weof_SOURCES = test-weof.c +test_weof_LDADD = ../libevent_core.la +test_time_SOURCES = test-time.c +test_time_LDADD = ../libevent_core.la +regress_SOURCES = regress.c regress.h regress_http.c regress_dns.c \ + regress_rpc.c \ + regress.gen.c regress.gen.h +regress_LDADD = ../libevent.la +bench_SOURCES = bench.c +bench_LDADD = ../libevent.la + +regress.gen.c regress.gen.h: regress.rpc $(top_srcdir)/event_rpcgen.py + $(top_srcdir)/event_rpcgen.py $(srcdir)/regress.rpc || echo "No Python installed" + +DISTCLEANFILES = *~ + +test: test-init test-eof test-weof test-time regress + +verify: test + @$(srcdir)/test.sh + +bench test-init test-eof test-weof test-time: ../libevent.la diff --git a/libevent/test/bench.c b/libevent/test/bench.c new file mode 100644 index 00000000000..c976932fa80 --- /dev/null +++ b/libevent/test/bench.c @@ -0,0 +1,188 @@ +/* + * Copyright 2003 Niels Provos + * 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. + * 4. 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. + * + * + * Mon 03/10/2003 - Modified by Davide Libenzi + * + * Added chain event propagation to improve the sensitivity of + * the measure respect to the event loop efficency. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + + +static int count, writes, fired; +static int *pipes; +static int num_pipes, num_active, num_writes; +static struct event *events; + +static void +read_cb(int fd, short which, void *arg) +{ + long idx = (long) arg, widx = idx + 1; + u_char ch; + + count += read(fd, &ch, sizeof(ch)); + if (writes) { + if (widx >= num_pipes) + widx -= num_pipes; + write(pipes[2 * widx + 1], "e", 1); + writes--; + fired++; + } +} + +static struct timeval * +run_once(void) +{ + int *cp, space; + long i; + static struct timeval ts, te; + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { + event_del(&events[i]); + event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *) i); + event_add(&events[i], NULL); + } + + event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + + fired = 0; + space = num_pipes / num_active; + space = space * 2; + for (i = 0; i < num_active; i++, fired++) + write(pipes[i * space + 1], "e", 1); + + count = 0; + writes = num_writes; + { int xcount = 0; + gettimeofday(&ts, NULL); + do { + event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK); + xcount++; + } while (count != fired); + gettimeofday(&te, NULL); + + if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count); + } + + evutil_timersub(&te, &ts, &te); + + return (&te); +} + +int +main (int argc, char **argv) +{ +#ifndef WIN32 + struct rlimit rl; +#endif + int i, c; + struct timeval *tv; + int *cp; + + num_pipes = 100; + num_active = 1; + num_writes = num_pipes; + while ((c = getopt(argc, argv, "n:a:w:")) != -1) { + switch (c) { + case 'n': + num_pipes = atoi(optarg); + break; + case 'a': + num_active = atoi(optarg); + break; + case 'w': + num_writes = atoi(optarg); + break; + default: + fprintf(stderr, "Illegal argument \"%c\"\n", c); + exit(1); + } + } + +#ifndef WIN32 + rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50; + if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { + perror("setrlimit"); + exit(1); + } +#endif + + events = calloc(num_pipes, sizeof(struct event)); + pipes = calloc(num_pipes * 2, sizeof(int)); + if (events == NULL || pipes == NULL) { + perror("malloc"); + exit(1); + } + + event_init(); + + for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) { +#ifdef USE_PIPES + if (pipe(cp) == -1) { +#else + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) { +#endif + perror("pipe"); + exit(1); + } + } + + for (i = 0; i < 25; i++) { + tv = run_once(); + if (tv == NULL) + exit(1); + fprintf(stdout, "%ld\n", + tv->tv_sec * 1000000L + tv->tv_usec); + } + + exit(0); +} diff --git a/libevent/test/regress.c b/libevent/test/regress.c new file mode 100644 index 00000000000..0b7517d3aa4 --- /dev/null +++ b/libevent/test/regress.c @@ -0,0 +1,1703 @@ +/* + * Copyright (c) 2003, 2004 Niels Provos + * 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 WIN32 +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "evutil.h" +#include "event-internal.h" +#include "log.h" + +#include "regress.h" +#ifndef WIN32 +#include "regress.gen.h" +#endif + +int pair[2]; +int test_ok; +static int called; +static char wbuf[4096]; +static char rbuf[4096]; +static int woff; +static int roff; +static int usepersist; +static struct timeval tset; +static struct timeval tcalled; +static struct event_base *global_base; + +#define TEST1 "this is a test" +#define SECONDS 1 + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifdef WIN32 +#define write(fd,buf,len) send((fd),(buf),(len),0) +#define read(fd,buf,len) recv((fd),(buf),(len),0) +#endif + +static void +simple_read_cb(int fd, short event, void *arg) +{ + char buf[256]; + int len; + + if (arg == NULL) + return; + + len = read(fd, buf, sizeof(buf)); + + if (len) { + if (!called) { + if (event_add(arg, NULL) == -1) + exit(1); + } + } else if (called == 1) + test_ok = 1; + + called++; +} + +static void +simple_write_cb(int fd, short event, void *arg) +{ + int len; + + if (arg == NULL) + return; + + len = write(fd, TEST1, strlen(TEST1) + 1); + if (len == -1) + test_ok = 0; + else + test_ok = 1; +} + +static void +multiple_write_cb(int fd, short event, void *arg) +{ + struct event *ev = arg; + int len; + + len = 128; + if (woff + len >= sizeof(wbuf)) + len = sizeof(wbuf) - woff; + + len = write(fd, wbuf + woff, len); + if (len == -1) { + fprintf(stderr, "%s: write\n", __func__); + if (usepersist) + event_del(ev); + return; + } + + woff += len; + + if (woff >= sizeof(wbuf)) { + shutdown(fd, SHUT_WR); + if (usepersist) + event_del(ev); + return; + } + + if (!usepersist) { + if (event_add(ev, NULL) == -1) + exit(1); + } +} + +static void +multiple_read_cb(int fd, short event, void *arg) +{ + struct event *ev = arg; + int len; + + len = read(fd, rbuf + roff, sizeof(rbuf) - roff); + if (len == -1) + fprintf(stderr, "%s: read\n", __func__); + if (len <= 0) { + if (usepersist) + event_del(ev); + return; + } + + roff += len; + if (!usepersist) { + if (event_add(ev, NULL) == -1) + exit(1); + } +} + +static void +timeout_cb(int fd, short event, void *arg) +{ + struct timeval tv; + int diff; + + evutil_gettimeofday(&tcalled, NULL); + if (evutil_timercmp(&tcalled, &tset, >)) + evutil_timersub(&tcalled, &tset, &tv); + else + evutil_timersub(&tset, &tcalled, &tv); + + diff = tv.tv_sec*1000 + tv.tv_usec/1000 - SECONDS * 1000; + if (diff < 0) + diff = -diff; + + if (diff < 100) + test_ok = 1; +} + +#ifndef WIN32 +static void +signal_cb_sa(int sig) +{ + test_ok = 2; +} + +static void +signal_cb(int fd, short event, void *arg) +{ + struct event *ev = arg; + + signal_del(ev); + test_ok = 1; +} +#endif + +struct both { + struct event ev; + int nread; +}; + +static void +combined_read_cb(int fd, short event, void *arg) +{ + struct both *both = arg; + char buf[128]; + int len; + + len = read(fd, buf, sizeof(buf)); + if (len == -1) + fprintf(stderr, "%s: read\n", __func__); + if (len <= 0) + return; + + both->nread += len; + if (event_add(&both->ev, NULL) == -1) + exit(1); +} + +static void +combined_write_cb(int fd, short event, void *arg) +{ + struct both *both = arg; + char buf[128]; + int len; + + len = sizeof(buf); + if (len > both->nread) + len = both->nread; + + len = write(fd, buf, len); + if (len == -1) + fprintf(stderr, "%s: write\n", __func__); + if (len <= 0) { + shutdown(fd, SHUT_WR); + return; + } + + both->nread -= len; + if (event_add(&both->ev, NULL) == -1) + exit(1); +} + +/* Test infrastructure */ + +static int +setup_test(const char *name) +{ + + fprintf(stdout, "%s", name); + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + fprintf(stderr, "%s: socketpair\n", __func__); + exit(1); + } + +#ifdef HAVE_FCNTL + if (fcntl(pair[0], F_SETFL, O_NONBLOCK) == -1) + fprintf(stderr, "fcntl(O_NONBLOCK)"); + + if (fcntl(pair[1], F_SETFL, O_NONBLOCK) == -1) + fprintf(stderr, "fcntl(O_NONBLOCK)"); +#endif + + test_ok = 0; + called = 0; + return (0); +} + +static int +cleanup_test(void) +{ +#ifndef WIN32 + close(pair[0]); + close(pair[1]); +#else + CloseHandle((HANDLE)pair[0]); + CloseHandle((HANDLE)pair[1]); +#endif + if (test_ok) + fprintf(stdout, "OK\n"); + else { + fprintf(stdout, "FAILED\n"); + exit(1); + } + test_ok = 0; + return (0); +} + +static void +test_registerfds(void) +{ + int i, j; + int pair[2]; + struct event read_evs[512]; + struct event write_evs[512]; + + struct event_base *base = event_base_new(); + + fprintf(stdout, "Testing register fds: "); + + for (i = 0; i < 512; ++i) { + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) { + /* run up to the limit of file descriptors */ + break; + } + event_set(&read_evs[i], pair[0], + EV_READ|EV_PERSIST, simple_read_cb, NULL); + event_base_set(base, &read_evs[i]); + event_add(&read_evs[i], NULL); + event_set(&write_evs[i], pair[1], + EV_WRITE|EV_PERSIST, simple_write_cb, NULL); + event_base_set(base, &write_evs[i]); + event_add(&write_evs[i], NULL); + + /* just loop once */ + event_base_loop(base, EVLOOP_ONCE); + } + + /* now delete everything */ + for (j = 0; j < i; ++j) { + event_del(&read_evs[j]); + event_del(&write_evs[j]); +#ifndef WIN32 + close(read_evs[j].ev_fd); + close(write_evs[j].ev_fd); +#else + CloseHandle((HANDLE)read_evs[j].ev_fd); + CloseHandle((HANDLE)write_evs[j].ev_fd); +#endif + + /* just loop once */ + event_base_loop(base, EVLOOP_ONCE); + } + + event_base_free(base); + + fprintf(stdout, "OK\n"); +} + +static void +test_simpleread(void) +{ + struct event ev; + + /* Very simple read test */ + setup_test("Simple read: "); + + write(pair[0], TEST1, strlen(TEST1)+1); + shutdown(pair[0], SHUT_WR); + + event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +static void +test_simplewrite(void) +{ + struct event ev; + + /* Very simple write test */ + setup_test("Simple write: "); + + event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +static void +test_multiple(void) +{ + struct event ev, ev2; + int i; + + /* Multiple read and write test */ + setup_test("Multiple read/write: "); + memset(rbuf, 0, sizeof(rbuf)); + for (i = 0; i < sizeof(wbuf); i++) + wbuf[i] = i; + + roff = woff = 0; + usepersist = 0; + + event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2); + if (event_add(&ev2, NULL) == -1) + exit(1); + event_dispatch(); + + if (roff == woff) + test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; + + cleanup_test(); +} + +static void +test_persistent(void) +{ + struct event ev, ev2; + int i; + + /* Multiple read and write test with persist */ + setup_test("Persist read/write: "); + memset(rbuf, 0, sizeof(rbuf)); + for (i = 0; i < sizeof(wbuf); i++) + wbuf[i] = i; + + roff = woff = 0; + usepersist = 1; + + event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2); + if (event_add(&ev2, NULL) == -1) + exit(1); + event_dispatch(); + + if (roff == woff) + test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0; + + cleanup_test(); +} + +static void +test_combined(void) +{ + struct both r1, r2, w1, w2; + + setup_test("Combined read/write: "); + memset(&r1, 0, sizeof(r1)); + memset(&r2, 0, sizeof(r2)); + memset(&w1, 0, sizeof(w1)); + memset(&w2, 0, sizeof(w2)); + + w1.nread = 4096; + w2.nread = 8192; + + event_set(&r1.ev, pair[0], EV_READ, combined_read_cb, &r1); + event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1); + event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2); + event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2); + if (event_add(&r1.ev, NULL) == -1) + exit(1); + if (event_add(&w1.ev, NULL)) + exit(1); + if (event_add(&r2.ev, NULL)) + exit(1); + if (event_add(&w2.ev, NULL)) + exit(1); + + event_dispatch(); + + if (r1.nread == 8192 && r2.nread == 4096) + test_ok = 1; + + cleanup_test(); +} + +static void +test_simpletimeout(void) +{ + struct timeval tv; + struct event ev; + + setup_test("Simple timeout: "); + + tv.tv_usec = 0; + tv.tv_sec = SECONDS; + evtimer_set(&ev, timeout_cb, NULL); + evtimer_add(&ev, &tv); + + evutil_gettimeofday(&tset, NULL); + event_dispatch(); + + cleanup_test(); +} + +#ifndef WIN32 +extern struct event_base *current_base; + +static void +child_signal_cb(int fd, short event, void *arg) +{ + struct timeval tv; + int *pint = arg; + + *pint = 1; + + tv.tv_usec = 500000; + tv.tv_sec = 0; + event_loopexit(&tv); +} + +static void +test_fork(void) +{ + int status, got_sigchld = 0; + struct event ev, sig_ev; + pid_t pid; + + setup_test("After fork: "); + + write(pair[0], TEST1, strlen(TEST1)+1); + + event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + + signal_set(&sig_ev, SIGCHLD, child_signal_cb, &got_sigchld); + signal_add(&sig_ev, NULL); + + if ((pid = fork()) == 0) { + /* in the child */ + if (event_reinit(current_base) == -1) { + fprintf(stderr, "FAILED (reinit)\n"); + exit(1); + } + + signal_del(&sig_ev); + + called = 0; + + event_dispatch(); + + /* we do not send an EOF; simple_read_cb requires an EOF + * to set test_ok. we just verify that the callback was + * called. */ + exit(test_ok != 0 || called != 2 ? -2 : 76); + } + + /* wait for the child to read the data */ + sleep(1); + + write(pair[0], TEST1, strlen(TEST1)+1); + + if (waitpid(pid, &status, 0) == -1) { + fprintf(stderr, "FAILED (fork)\n"); + exit(1); + } + + if (WEXITSTATUS(status) != 76) { + fprintf(stderr, "FAILED (exit): %d\n", WEXITSTATUS(status)); + exit(1); + } + + /* test that the current event loop still works */ + write(pair[0], TEST1, strlen(TEST1)+1); + shutdown(pair[0], SHUT_WR); + + event_dispatch(); + + if (!got_sigchld) { + fprintf(stdout, "FAILED (sigchld)\n"); + exit(1); + } + + signal_del(&sig_ev); + + cleanup_test(); +} + +static void +test_simplesignal(void) +{ + struct event ev; + struct itimerval itv; + + setup_test("Simple signal: "); + signal_set(&ev, SIGALRM, signal_cb, &ev); + signal_add(&ev, NULL); + /* find bugs in which operations are re-ordered */ + signal_del(&ev); + signal_add(&ev, NULL); + + memset(&itv, 0, sizeof(itv)); + itv.it_value.tv_sec = 1; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + goto skip_simplesignal; + + event_dispatch(); + skip_simplesignal: + if (signal_del(&ev) == -1) + test_ok = 0; + + cleanup_test(); +} + +static void +test_multiplesignal(void) +{ + struct event ev_one, ev_two; + struct itimerval itv; + + setup_test("Multiple signal: "); + + signal_set(&ev_one, SIGALRM, signal_cb, &ev_one); + signal_add(&ev_one, NULL); + + signal_set(&ev_two, SIGALRM, signal_cb, &ev_two); + signal_add(&ev_two, NULL); + + memset(&itv, 0, sizeof(itv)); + itv.it_value.tv_sec = 1; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + goto skip_simplesignal; + + event_dispatch(); + + skip_simplesignal: + if (signal_del(&ev_one) == -1) + test_ok = 0; + if (signal_del(&ev_two) == -1) + test_ok = 0; + + cleanup_test(); +} + +static void +test_immediatesignal(void) +{ + struct event ev; + + test_ok = 0; + printf("Immediate signal: "); + signal_set(&ev, SIGUSR1, signal_cb, &ev); + signal_add(&ev, NULL); + raise(SIGUSR1); + event_loop(EVLOOP_NONBLOCK); + signal_del(&ev); + cleanup_test(); +} + +static void +test_signal_dealloc(void) +{ + /* make sure that signal_event is event_del'ed and pipe closed */ + struct event ev; + struct event_base *base = event_init(); + printf("Signal dealloc: "); + signal_set(&ev, SIGUSR1, signal_cb, &ev); + signal_add(&ev, NULL); + signal_del(&ev); + event_base_free(base); + /* If we got here without asserting, we're fine. */ + test_ok = 1; + cleanup_test(); +} + +static void +test_signal_pipeloss(void) +{ + /* make sure that the base1 pipe is closed correctly. */ + struct event_base *base1, *base2; + int pipe1; + test_ok = 0; + printf("Signal pipeloss: "); + base1 = event_init(); + pipe1 = base1->sig.ev_signal_pair[0]; + base2 = event_init(); + event_base_free(base2); + event_base_free(base1); + if (close(pipe1) != -1 || errno!=EBADF) { + /* fd must be closed, so second close gives -1, EBADF */ + printf("signal pipe not closed. "); + test_ok = 0; + } else { + test_ok = 1; + } + cleanup_test(); +} + +/* + * make two bases to catch signals, use both of them. this only works + * for event mechanisms that use our signal pipe trick. kqueue handles + * signals internally, and all interested kqueues get all the signals. + */ +static void +test_signal_switchbase(void) +{ + struct event ev1, ev2; + struct event_base *base1, *base2; + int is_kqueue; + test_ok = 0; + printf("Signal switchbase: "); + base1 = event_init(); + base2 = event_init(); + is_kqueue = !strcmp(event_get_method(),"kqueue"); + signal_set(&ev1, SIGUSR1, signal_cb, &ev1); + signal_set(&ev2, SIGUSR1, signal_cb, &ev2); + if (event_base_set(base1, &ev1) || + event_base_set(base2, &ev2) || + event_add(&ev1, NULL) || + event_add(&ev2, NULL)) { + fprintf(stderr, "%s: cannot set base, add\n", __func__); + exit(1); + } + + test_ok = 0; + /* can handle signal before loop is called */ + raise(SIGUSR1); + event_base_loop(base2, EVLOOP_NONBLOCK); + if (is_kqueue) { + if (!test_ok) + goto done; + test_ok = 0; + } + event_base_loop(base1, EVLOOP_NONBLOCK); + if (test_ok && !is_kqueue) { + test_ok = 0; + + /* set base1 to handle signals */ + event_base_loop(base1, EVLOOP_NONBLOCK); + raise(SIGUSR1); + event_base_loop(base1, EVLOOP_NONBLOCK); + event_base_loop(base2, EVLOOP_NONBLOCK); + } + done: + event_base_free(base1); + event_base_free(base2); + cleanup_test(); +} + +/* + * assert that a signal event removed from the event queue really is + * removed - with no possibility of it's parent handler being fired. + */ +static void +test_signal_assert(void) +{ + struct event ev; + struct event_base *base = event_init(); + test_ok = 0; + printf("Signal handler assert: "); + /* use SIGCONT so we don't kill ourselves when we signal to nowhere */ + signal_set(&ev, SIGCONT, signal_cb, &ev); + signal_add(&ev, NULL); + /* + * if signal_del() fails to reset the handler, it's current handler + * will still point to evsignal_handler(). + */ + signal_del(&ev); + + raise(SIGCONT); + /* only way to verify we were in evsignal_handler() */ + if (base->sig.evsignal_caught) + test_ok = 0; + else + test_ok = 1; + + event_base_free(base); + cleanup_test(); + return; +} + +/* + * assert that we restore our previous signal handler properly. + */ +static void +test_signal_restore(void) +{ + struct event ev; + struct event_base *base = event_init(); +#ifdef HAVE_SIGACTION + struct sigaction sa; +#endif + + test_ok = 0; + printf("Signal handler restore: "); +#ifdef HAVE_SIGACTION + sa.sa_handler = signal_cb_sa; + sa.sa_flags = 0x0; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGUSR1, &sa, NULL) == -1) + goto out; +#else + if (signal(SIGUSR1, signal_cb_sa) == SIG_ERR) + goto out; +#endif + signal_set(&ev, SIGUSR1, signal_cb, &ev); + signal_add(&ev, NULL); + signal_del(&ev); + + raise(SIGUSR1); + /* 1 == signal_cb, 2 == signal_cb_sa, we want our previous handler */ + if (test_ok != 2) + test_ok = 0; +out: + event_base_free(base); + cleanup_test(); + return; +} + +static void +signal_cb_swp(int sig, short event, void *arg) +{ + called++; + if (called < 5) + raise(sig); + else + event_loopexit(NULL); +} +static void +timeout_cb_swp(int fd, short event, void *arg) +{ + if (called == -1) { + struct timeval tv = {5, 0}; + + called = 0; + evtimer_add((struct event *)arg, &tv); + raise(SIGUSR1); + return; + } + test_ok = 0; + event_loopexit(NULL); +} + +static void +test_signal_while_processing(void) +{ + struct event_base *base = event_init(); + struct event ev, ev_timer; + struct timeval tv = {0, 0}; + + setup_test("Receiving a signal while processing other signal: "); + + called = -1; + test_ok = 1; + signal_set(&ev, SIGUSR1, signal_cb_swp, NULL); + signal_add(&ev, NULL); + evtimer_set(&ev_timer, timeout_cb_swp, &ev_timer); + evtimer_add(&ev_timer, &tv); + event_dispatch(); + + event_base_free(base); + cleanup_test(); + return; +} +#endif + +static void +test_free_active_base(void) +{ + struct event_base *base1; + struct event ev1; + setup_test("Free active base: "); + base1 = event_init(); + event_set(&ev1, pair[1], EV_READ, simple_read_cb, &ev1); + event_base_set(base1, &ev1); + event_add(&ev1, NULL); + /* event_del(&ev1); */ + event_base_free(base1); + test_ok = 1; + cleanup_test(); +} + +static void +test_event_base_new(void) +{ + struct event_base *base; + struct event ev1; + setup_test("Event base new: "); + + write(pair[0], TEST1, strlen(TEST1)+1); + shutdown(pair[0], SHUT_WR); + + base = event_base_new(); + event_set(&ev1, pair[1], EV_READ, simple_read_cb, &ev1); + event_base_set(base, &ev1); + event_add(&ev1, NULL); + + event_base_dispatch(base); + + event_base_free(base); + test_ok = 1; + cleanup_test(); +} + +static void +test_loopexit(void) +{ + struct timeval tv, tv_start, tv_end; + struct event ev; + + setup_test("Loop exit: "); + + tv.tv_usec = 0; + tv.tv_sec = 60*60*24; + evtimer_set(&ev, timeout_cb, NULL); + evtimer_add(&ev, &tv); + + tv.tv_usec = 0; + tv.tv_sec = 1; + event_loopexit(&tv); + + evutil_gettimeofday(&tv_start, NULL); + event_dispatch(); + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + + evtimer_del(&ev); + + if (tv.tv_sec < 2) + test_ok = 1; + + cleanup_test(); +} + +static void +test_loopexit_multiple(void) +{ + struct timeval tv; + struct event_base *base; + + setup_test("Loop Multiple exit: "); + + base = event_base_new(); + + tv.tv_usec = 0; + tv.tv_sec = 1; + event_base_loopexit(base, &tv); + + tv.tv_usec = 0; + tv.tv_sec = 2; + event_base_loopexit(base, &tv); + + event_base_dispatch(base); + + event_base_free(base); + + test_ok = 1; + + cleanup_test(); +} + +static void +break_cb(int fd, short events, void *arg) +{ + test_ok = 1; + event_loopbreak(); +} + +static void +fail_cb(int fd, short events, void *arg) +{ + test_ok = 0; +} + +static void +test_loopbreak(void) +{ + struct event ev1, ev2; + struct timeval tv; + + setup_test("Loop break: "); + + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&ev1, break_cb, NULL); + evtimer_add(&ev1, &tv); + evtimer_set(&ev2, fail_cb, NULL); + evtimer_add(&ev2, &tv); + + event_dispatch(); + + evtimer_del(&ev1); + evtimer_del(&ev2); + + cleanup_test(); +} + +static void +test_evbuffer(void) { + + struct evbuffer *evb = evbuffer_new(); + setup_test("Testing Evbuffer: "); + + evbuffer_add_printf(evb, "%s/%d", "hello", 1); + + if (EVBUFFER_LENGTH(evb) == 7 && + strcmp((char*)EVBUFFER_DATA(evb), "hello/1") == 0) + test_ok = 1; + + evbuffer_free(evb); + + cleanup_test(); +} + +static void +test_evbuffer_find(void) +{ + u_char* p; + const char* test1 = "1234567890\r\n"; + const char* test2 = "1234567890\r"; +#define EVBUFFER_INITIAL_LENGTH 256 + char test3[EVBUFFER_INITIAL_LENGTH]; + unsigned int i; + struct evbuffer * buf = evbuffer_new(); + + /* make sure evbuffer_find doesn't match past the end of the buffer */ + fprintf(stdout, "Testing evbuffer_find 1: "); + evbuffer_add(buf, (u_char*)test1, strlen(test1)); + evbuffer_drain(buf, strlen(test1)); + evbuffer_add(buf, (u_char*)test2, strlen(test2)); + p = evbuffer_find(buf, (u_char*)"\r\n", 2); + if (p == NULL) { + fprintf(stdout, "OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * drain the buffer and do another find; in r309 this would + * read past the allocated buffer causing a valgrind error. + */ + fprintf(stdout, "Testing evbuffer_find 2: "); + evbuffer_drain(buf, strlen(test2)); + for (i = 0; i < EVBUFFER_INITIAL_LENGTH; ++i) + test3[i] = 'a'; + test3[EVBUFFER_INITIAL_LENGTH - 1] = 'x'; + evbuffer_add(buf, (u_char *)test3, EVBUFFER_INITIAL_LENGTH); + p = evbuffer_find(buf, (u_char *)"xy", 2); + if (p == NULL) { + printf("OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* simple test for match at end of allocated buffer */ + fprintf(stdout, "Testing evbuffer_find 3: "); + p = evbuffer_find(buf, (u_char *)"ax", 2); + if (p != NULL && strncmp((char*)p, "ax", 2) == 0) { + printf("OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + evbuffer_free(buf); +} + +/* + * simple bufferevent test + */ + +static void +readcb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(bev->input) == 8333) { + bufferevent_disable(bev, EV_READ); + test_ok++; + } +} + +static void +writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(bev->output) == 0) + test_ok++; +} + +static void +errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; +} + +static void +test_bufferevent(void) +{ + struct bufferevent *bev1, *bev2; + char buffer[8333]; + int i; + + setup_test("Bufferevent: "); + + bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL); + bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL); + + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = i; + + bufferevent_write(bev1, buffer, sizeof(buffer)); + + event_dispatch(); + + bufferevent_free(bev1); + bufferevent_free(bev2); + + if (test_ok != 2) + test_ok = 0; + + cleanup_test(); +} + +/* + * test watermarks and bufferevent + */ + +static void +wm_readcb(struct bufferevent *bev, void *arg) +{ + int len = EVBUFFER_LENGTH(bev->input); + static int nread; + + assert(len >= 10 && len <= 20); + + evbuffer_drain(bev->input, len); + + nread += len; + if (nread == 65000) { + bufferevent_disable(bev, EV_READ); + test_ok++; + } +} + +static void +wm_writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(bev->output) == 0) + test_ok++; +} + +static void +wm_errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; +} + +static void +test_bufferevent_watermarks(void) +{ + struct bufferevent *bev1, *bev2; + char buffer[65000]; + int i; + + setup_test("Bufferevent Watermarks: "); + + bev1 = bufferevent_new(pair[0], NULL, wm_writecb, wm_errorcb, NULL); + bev2 = bufferevent_new(pair[1], wm_readcb, NULL, wm_errorcb, NULL); + + bufferevent_disable(bev1, EV_READ); + bufferevent_enable(bev2, EV_READ); + + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = i; + + bufferevent_write(bev1, buffer, sizeof(buffer)); + + /* limit the reading on the receiving bufferevent */ + bufferevent_setwatermark(bev2, EV_READ, 10, 20); + + event_dispatch(); + + bufferevent_free(bev1); + bufferevent_free(bev2); + + if (test_ok != 2) + test_ok = 0; + + cleanup_test(); +} + +struct test_pri_event { + struct event ev; + int count; +}; + +static void +test_priorities_cb(int fd, short what, void *arg) +{ + struct test_pri_event *pri = arg; + struct timeval tv; + + if (pri->count == 3) { + event_loopexit(NULL); + return; + } + + pri->count++; + + evutil_timerclear(&tv); + event_add(&pri->ev, &tv); +} + +static void +test_priorities(int npriorities) +{ + char buf[32]; + struct test_pri_event one, two; + struct timeval tv; + + evutil_snprintf(buf, sizeof(buf), "Testing Priorities %d: ", npriorities); + setup_test(buf); + + event_base_priority_init(global_base, npriorities); + + memset(&one, 0, sizeof(one)); + memset(&two, 0, sizeof(two)); + + timeout_set(&one.ev, test_priorities_cb, &one); + if (event_priority_set(&one.ev, 0) == -1) { + fprintf(stderr, "%s: failed to set priority", __func__); + exit(1); + } + + timeout_set(&two.ev, test_priorities_cb, &two); + if (event_priority_set(&two.ev, npriorities - 1) == -1) { + fprintf(stderr, "%s: failed to set priority", __func__); + exit(1); + } + + evutil_timerclear(&tv); + + if (event_add(&one.ev, &tv) == -1) + exit(1); + if (event_add(&two.ev, &tv) == -1) + exit(1); + + event_dispatch(); + + event_del(&one.ev); + event_del(&two.ev); + + if (npriorities == 1) { + if (one.count == 3 && two.count == 3) + test_ok = 1; + } else if (npriorities == 2) { + /* Two is called once because event_loopexit is priority 1 */ + if (one.count == 3 && two.count == 1) + test_ok = 1; + } else { + if (one.count == 3 && two.count == 0) + test_ok = 1; + } + + cleanup_test(); +} + +static void +test_multiple_cb(int fd, short event, void *arg) +{ + if (event & EV_READ) + test_ok |= 1; + else if (event & EV_WRITE) + test_ok |= 2; +} + +static void +test_multiple_events_for_same_fd(void) +{ + struct event e1, e2; + + setup_test("Multiple events for same fd: "); + + event_set(&e1, pair[0], EV_READ, test_multiple_cb, NULL); + event_add(&e1, NULL); + event_set(&e2, pair[0], EV_WRITE, test_multiple_cb, NULL); + event_add(&e2, NULL); + event_loop(EVLOOP_ONCE); + event_del(&e2); + write(pair[1], TEST1, strlen(TEST1)+1); + event_loop(EVLOOP_ONCE); + event_del(&e1); + + if (test_ok != 3) + test_ok = 0; + + cleanup_test(); +} + +int evtag_decode_int(uint32_t *pnumber, struct evbuffer *evbuf); +int evtag_encode_tag(struct evbuffer *evbuf, uint32_t number); +int evtag_decode_tag(uint32_t *pnumber, struct evbuffer *evbuf); + +static void +read_once_cb(int fd, short event, void *arg) +{ + char buf[256]; + int len; + + len = read(fd, buf, sizeof(buf)); + + if (called) { + test_ok = 0; + } else if (len) { + /* Assumes global pair[0] can be used for writing */ + write(pair[0], TEST1, strlen(TEST1)+1); + test_ok = 1; + } + + called++; +} + +static void +test_want_only_once(void) +{ + struct event ev; + struct timeval tv; + + /* Very simple read test */ + setup_test("Want read only once: "); + + write(pair[0], TEST1, strlen(TEST1)+1); + + /* Setup the loop termination */ + evutil_timerclear(&tv); + tv.tv_sec = 1; + event_loopexit(&tv); + + event_set(&ev, pair[1], EV_READ, read_once_cb, &ev); + if (event_add(&ev, NULL) == -1) + exit(1); + event_dispatch(); + + cleanup_test(); +} + +#define TEST_MAX_INT 6 + +static void +evtag_int_test(void) +{ + struct evbuffer *tmp = evbuffer_new(); + uint32_t integers[TEST_MAX_INT] = { + 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 + }; + uint32_t integer; + int i; + + for (i = 0; i < TEST_MAX_INT; i++) { + int oldlen, newlen; + oldlen = EVBUFFER_LENGTH(tmp); + encode_int(tmp, integers[i]); + newlen = EVBUFFER_LENGTH(tmp); + fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n", + integers[i], newlen - oldlen); + } + + for (i = 0; i < TEST_MAX_INT; i++) { + if (evtag_decode_int(&integer, tmp) == -1) { + fprintf(stderr, "decode %d failed", i); + exit(1); + } + if (integer != integers[i]) { + fprintf(stderr, "got %x, wanted %x", + integer, integers[i]); + exit(1); + } + } + + if (EVBUFFER_LENGTH(tmp) != 0) { + fprintf(stderr, "trailing data"); + exit(1); + } + evbuffer_free(tmp); + + fprintf(stdout, "\t%s: OK\n", __func__); +} + +static void +evtag_fuzz(void) +{ + u_char buffer[4096]; + struct evbuffer *tmp = evbuffer_new(); + struct timeval tv; + int i, j; + + int not_failed = 0; + for (j = 0; j < 100; j++) { + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = rand(); + evbuffer_drain(tmp, -1); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) + not_failed++; + } + + /* The majority of decodes should fail */ + if (not_failed >= 10) { + fprintf(stderr, "evtag_unmarshal should have failed"); + exit(1); + } + + /* Now insert some corruption into the tag length field */ + evbuffer_drain(tmp, -1); + evutil_timerclear(&tv); + tv.tv_sec = 1; + evtag_marshal_timeval(tmp, 0, &tv); + evbuffer_add(tmp, buffer, sizeof(buffer)); + + EVBUFFER_DATA(tmp)[1] = 0xff; + if (evtag_unmarshal_timeval(tmp, 0, &tv) != -1) { + fprintf(stderr, "evtag_unmarshal_timeval should have failed"); + exit(1); + } + + evbuffer_free(tmp); + + fprintf(stdout, "\t%s: OK\n", __func__); +} + +static void +evtag_tag_encoding(void) +{ + struct evbuffer *tmp = evbuffer_new(); + uint32_t integers[TEST_MAX_INT] = { + 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 + }; + uint32_t integer; + int i; + + for (i = 0; i < TEST_MAX_INT; i++) { + int oldlen, newlen; + oldlen = EVBUFFER_LENGTH(tmp); + evtag_encode_tag(tmp, integers[i]); + newlen = EVBUFFER_LENGTH(tmp); + fprintf(stdout, "\t\tencoded 0x%08x with %d bytes\n", + integers[i], newlen - oldlen); + } + + for (i = 0; i < TEST_MAX_INT; i++) { + if (evtag_decode_tag(&integer, tmp) == -1) { + fprintf(stderr, "decode %d failed", i); + exit(1); + } + if (integer != integers[i]) { + fprintf(stderr, "got %x, wanted %x", + integer, integers[i]); + exit(1); + } + } + + if (EVBUFFER_LENGTH(tmp) != 0) { + fprintf(stderr, "trailing data"); + exit(1); + } + evbuffer_free(tmp); + + fprintf(stdout, "\t%s: OK\n", __func__); +} + +static void +evtag_test(void) +{ + fprintf(stdout, "Testing Tagging:\n"); + + evtag_init(); + evtag_int_test(); + evtag_fuzz(); + + evtag_tag_encoding(); + + fprintf(stdout, "OK\n"); +} + +#ifndef WIN32 +static void +rpc_test(void) +{ + struct msg *msg, *msg2; + struct kill *attack; + struct run *run; + struct evbuffer *tmp = evbuffer_new(); + struct timeval tv_start, tv_end; + uint32_t tag; + int i; + + fprintf(stdout, "Testing RPC: "); + + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "phoenix"); + + if (EVTAG_GET(msg, attack, &attack) == -1) { + fprintf(stderr, "Failed to set kill message.\n"); + exit(1); + } + + EVTAG_ASSIGN(attack, weapon, "feather"); + EVTAG_ASSIGN(attack, action, "tickle"); + + evutil_gettimeofday(&tv_start, NULL); + for (i = 0; i < 1000; ++i) { + run = EVTAG_ADD(msg, run); + if (run == NULL) { + fprintf(stderr, "Failed to add run message.\n"); + exit(1); + } + EVTAG_ASSIGN(run, how, "very fast but with some data in it"); + EVTAG_ASSIGN(run, fixed_bytes, + (unsigned char*)"012345678901234567890123"); + } + + if (msg_complete(msg) == -1) { + fprintf(stderr, "Failed to make complete message.\n"); + exit(1); + } + + evtag_marshal_msg(tmp, 0xdeaf, msg); + + if (evtag_peek(tmp, &tag) == -1) { + fprintf(stderr, "Failed to peak tag.\n"); + exit (1); + } + + if (tag != 0xdeaf) { + fprintf(stderr, "Got incorrect tag: %0x.\n", tag); + exit (1); + } + + msg2 = msg_new(); + if (evtag_unmarshal_msg(tmp, 0xdeaf, msg2) == -1) { + fprintf(stderr, "Failed to unmarshal message.\n"); + exit(1); + } + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + fprintf(stderr, "(%.1f us/add) ", + (float)tv_end.tv_sec/(float)i * 1000000.0 + + tv_end.tv_usec / (float)i); + + if (!EVTAG_HAS(msg2, from_name) || + !EVTAG_HAS(msg2, to_name) || + !EVTAG_HAS(msg2, attack)) { + fprintf(stderr, "Missing data structures.\n"); + exit(1); + } + + if (EVTAG_LEN(msg2, run) != i) { + fprintf(stderr, "Wrong number of run messages.\n"); + exit(1); + } + + msg_free(msg); + msg_free(msg2); + + evbuffer_free(tmp); + + fprintf(stdout, "OK\n"); +} +#endif + +static void +test_evutil_strtoll(void) +{ + const char *s; + char *endptr; + setup_test("evutil_stroll: "); + test_ok = 0; + + if (evutil_strtoll("5000000000", NULL, 10) != ((ev_int64_t)5000000)*1000) + goto err; + if (evutil_strtoll("-5000000000", NULL, 10) != ((ev_int64_t)5000000)*-1000) + goto err; + s = " 99999stuff"; + if (evutil_strtoll(s, &endptr, 10) != (ev_int64_t)99999) + goto err; + if (endptr != s+6) + goto err; + if (evutil_strtoll("foo", NULL, 10) != 0) + goto err; + + test_ok = 1; + err: + cleanup_test(); +} + + +int +main (int argc, char **argv) +{ +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD( 2, 2 ); + + err = WSAStartup( wVersionRequested, &wsaData ); +#endif + +#ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); +#endif + setvbuf(stdout, NULL, _IONBF, 0); + + /* Initalize the event library */ + global_base = event_init(); + + test_registerfds(); + + test_evutil_strtoll(); + + /* use the global event base and need to be called first */ + test_priorities(1); + test_priorities(2); + test_priorities(3); + + test_evbuffer(); + test_evbuffer_find(); + + test_bufferevent(); + test_bufferevent_watermarks(); + + test_free_active_base(); + + test_event_base_new(); + + http_suite(); + +#ifndef WIN32 + rpc_suite(); +#endif + + dns_suite(); + +#ifndef WIN32 + test_fork(); +#endif + + test_simpleread(); + + test_simplewrite(); + + test_multiple(); + + test_persistent(); + + test_combined(); + + test_simpletimeout(); +#ifndef WIN32 + test_simplesignal(); + test_multiplesignal(); + test_immediatesignal(); +#endif + test_loopexit(); + test_loopbreak(); + + test_loopexit_multiple(); + + test_multiple_events_for_same_fd(); + + test_want_only_once(); + + evtag_test(); + +#ifndef WIN32 + rpc_test(); + + test_signal_dealloc(); + test_signal_pipeloss(); + test_signal_switchbase(); + test_signal_restore(); + test_signal_assert(); + test_signal_while_processing(); +#endif + + return (0); +} + diff --git a/libevent/test/regress.gen.c b/libevent/test/regress.gen.c new file mode 100644 index 00000000000..ff31096a7c2 --- /dev/null +++ b/libevent/test/regress.gen.c @@ -0,0 +1,872 @@ +/* + * Automatically generated from ./regress.rpc + * by event_rpcgen.py/0.1. DO NOT EDIT THIS FILE. + */ + +#include +#include +#include +#include +#include +#include + + +#include "./regress.gen.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, ...); + + +/* + * Implementation of msg + */ + +static struct msg_access_ __msg_base = { + msg_from_name_assign, + msg_from_name_get, + msg_to_name_assign, + msg_to_name_get, + msg_attack_assign, + msg_attack_get, + msg_run_assign, + msg_run_get, + msg_run_add, +}; + +struct msg * +msg_new(void) +{ + struct msg *tmp; + if ((tmp = malloc(sizeof(struct msg))) == NULL) { + event_warn("%s: malloc", __func__); + return (NULL); + } + tmp->base = &__msg_base; + + tmp->from_name_data = NULL; + tmp->from_name_set = 0; + + tmp->to_name_data = NULL; + tmp->to_name_set = 0; + + tmp->attack_data = NULL; + tmp->attack_set = 0; + + tmp->run_data = NULL; + tmp->run_length = 0; + tmp->run_num_allocated = 0; + tmp->run_set = 0; + + return (tmp); +} + + + + +struct run * +msg_run_add(struct msg *msg) +{ + if (++msg->run_length >= msg->run_num_allocated) { + int tobe_allocated = msg->run_num_allocated; + struct run ** new_data = NULL; + tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1; + new_data = (struct run **) realloc(msg->run_data, + tobe_allocated * sizeof(struct run *)); + if (new_data == NULL) + goto error; + msg->run_data = new_data; + msg->run_num_allocated = tobe_allocated; + } + msg->run_data[msg->run_length - 1] = run_new(); + if (msg->run_data[msg->run_length - 1] == NULL) + goto error; + msg->run_set = 1; + return (msg->run_data[msg->run_length - 1]); +error: + --msg->run_length; + return (NULL); +} + + +int +msg_from_name_assign(struct msg *msg, + const char * value) +{ + if (msg->from_name_data != NULL) + free(msg->from_name_data); + if ((msg->from_name_data = strdup(value)) == NULL) + return (-1); + msg->from_name_set = 1; + return (0); +} + +int +msg_to_name_assign(struct msg *msg, + const char * value) +{ + if (msg->to_name_data != NULL) + free(msg->to_name_data); + if ((msg->to_name_data = strdup(value)) == NULL) + return (-1); + msg->to_name_set = 1; + return (0); +} + +int +msg_attack_assign(struct msg *msg, + const struct kill* value) +{ + struct evbuffer *tmp = NULL; + if (msg->attack_set) { + kill_clear(msg->attack_data); + msg->attack_set = 0; + } else { + msg->attack_data = kill_new(); + if (msg->attack_data == NULL) { + event_warn("%s: kill_new()", __func__); + goto error; + } + } + if ((tmp = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new()", __func__); + goto error; + } + kill_marshal(tmp, value); + if (kill_unmarshal(msg->attack_data, tmp) == -1) { + event_warnx("%s: kill_unmarshal", __func__); + goto error; + } + msg->attack_set = 1; + evbuffer_free(tmp); + return (0); + error: + if (tmp != NULL) + evbuffer_free(tmp); + if (msg->attack_data != NULL) { + kill_free(msg->attack_data); + msg->attack_data = NULL; + } + return (-1); +} + +int +msg_run_assign(struct msg *msg, int off, + const struct run * value) +{ + struct evbuffer *tmp = NULL; + if (!msg->run_set || off < 0 || off >= msg->run_length) + return (-1); + run_clear(msg->run_data[off]); + if ((tmp = evbuffer_new()) == NULL) { + event_warn("%s: evbuffer_new()", __func__); + goto error; + } + run_marshal(tmp, value); + if (run_unmarshal(msg->run_data[off], tmp) == -1) { + event_warnx("%s: run_unmarshal", __func__); + goto error; + } + evbuffer_free(tmp); + return (0); +error: + if (tmp != NULL) + evbuffer_free(tmp); + run_clear(msg->run_data[off]); + return (-1); +} + +int +msg_from_name_get(struct msg *msg, char * *value) +{ + if (msg->from_name_set != 1) + return (-1); + *value = msg->from_name_data; + return (0); +} + +int +msg_to_name_get(struct msg *msg, char * *value) +{ + if (msg->to_name_set != 1) + return (-1); + *value = msg->to_name_data; + return (0); +} + +int +msg_attack_get(struct msg *msg, struct kill* *value) +{ + if (msg->attack_set != 1) { + msg->attack_data = kill_new(); + if (msg->attack_data == NULL) + return (-1); + msg->attack_set = 1; + } + *value = msg->attack_data; + return (0); +} + +int +msg_run_get(struct msg *msg, int offset, + struct run * *value) +{ + if (!msg->run_set || offset < 0 || offset >= msg->run_length) + return (-1); + *value = msg->run_data[offset]; + return (0); +} + +void +msg_clear(struct msg *tmp) +{ + if (tmp->from_name_set == 1) { + free (tmp->from_name_data); + tmp->from_name_data = NULL; + tmp->from_name_set = 0; + } + if (tmp->to_name_set == 1) { + free (tmp->to_name_data); + tmp->to_name_data = NULL; + tmp->to_name_set = 0; + } + if (tmp->attack_set == 1) { + kill_free(tmp->attack_data); + tmp->attack_data = NULL; + tmp->attack_set = 0; + } + if (tmp->run_set == 1) { + int i; + for (i = 0; i < tmp->run_length; ++i) { + run_free(tmp->run_data[i]); + } + free(tmp->run_data); + tmp->run_data = NULL; + tmp->run_set = 0; + tmp->run_length = 0; + tmp->run_num_allocated = 0; + } +} + +void +msg_free(struct msg *tmp) +{ + if (tmp->from_name_data != NULL) + free (tmp->from_name_data); + if (tmp->to_name_data != NULL) + free (tmp->to_name_data); + if (tmp->attack_data != NULL) + kill_free(tmp->attack_data); + if (tmp->run_data != NULL) { + int i; + for (i = 0; i < tmp->run_length; ++i) { + run_free(tmp->run_data[i]); + tmp->run_data[i] = NULL; + } + free(tmp->run_data); + tmp->run_data = NULL; + tmp->run_length = 0; + tmp->run_num_allocated = 0; + } + free(tmp); +} + +void +msg_marshal(struct evbuffer *evbuf, const struct msg *tmp){ + evtag_marshal_string(evbuf, MSG_FROM_NAME, tmp->from_name_data); + evtag_marshal_string(evbuf, MSG_TO_NAME, tmp->to_name_data); + if (tmp->attack_set) { + evtag_marshal_kill(evbuf, MSG_ATTACK, tmp->attack_data); + } + { + int i; + for (i = 0; i < tmp->run_length; ++i) { + evtag_marshal_run(evbuf, MSG_RUN, tmp->run_data[i]); + } + } +} + +int +msg_unmarshal(struct msg *tmp, struct evbuffer *evbuf) +{ + uint32_t tag; + while (EVBUFFER_LENGTH(evbuf) > 0) { + if (evtag_peek(evbuf, &tag) == -1) + return (-1); + switch (tag) { + + case MSG_FROM_NAME: + + if (tmp->from_name_set) + return (-1); + if (evtag_unmarshal_string(evbuf, MSG_FROM_NAME, &tmp->from_name_data) == -1) { + event_warnx("%s: failed to unmarshal from_name", __func__); + return (-1); + } + tmp->from_name_set = 1; + break; + + case MSG_TO_NAME: + + if (tmp->to_name_set) + return (-1); + if (evtag_unmarshal_string(evbuf, MSG_TO_NAME, &tmp->to_name_data) == -1) { + event_warnx("%s: failed to unmarshal to_name", __func__); + return (-1); + } + tmp->to_name_set = 1; + break; + + case MSG_ATTACK: + + if (tmp->attack_set) + return (-1); + tmp->attack_data = kill_new(); + if (tmp->attack_data == NULL) + return (-1); + if (evtag_unmarshal_kill(evbuf, MSG_ATTACK, tmp->attack_data) == -1) { + event_warnx("%s: failed to unmarshal attack", __func__); + return (-1); + } + tmp->attack_set = 1; + break; + + case MSG_RUN: + + if (msg_run_add(tmp) == NULL) + return (-1); + if (evtag_unmarshal_run(evbuf, MSG_RUN, + tmp->run_data[tmp->run_length - 1]) == -1) { + --tmp->run_length; + event_warnx("%s: failed to unmarshal run", __func__); + return (-1); + } + tmp->run_set = 1; + break; + + default: + return -1; + } + } + + if (msg_complete(tmp) == -1) + return (-1); + return (0); +} + +int +msg_complete(struct msg *msg) +{ + if (!msg->from_name_set) + return (-1); + if (!msg->to_name_set) + return (-1); + if (msg->attack_set && kill_complete(msg->attack_data) == -1) + return (-1); + { + int i; + for (i = 0; i < msg->run_length; ++i) { + if (run_complete(msg->run_data[i]) == -1) + return (-1); + } + } + return (0); +} + +int +evtag_unmarshal_msg(struct evbuffer *evbuf, uint32_t need_tag, struct msg *msg) +{ + uint32_t tag; + int res = -1; + + struct evbuffer *tmp = evbuffer_new(); + + if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) + goto error; + + if (msg_unmarshal(msg, tmp) == -1) + goto error; + + res = 0; + + error: + evbuffer_free(tmp); + return (res); +} + +void +evtag_marshal_msg(struct evbuffer *evbuf, uint32_t tag, const struct msg *msg) +{ + struct evbuffer *_buf = evbuffer_new(); + assert(_buf != NULL); + evbuffer_drain(_buf, -1); + msg_marshal(_buf, msg); + evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), EVBUFFER_LENGTH(_buf)); + evbuffer_free(_buf); +} + +/* + * Implementation of kill + */ + +static struct kill_access_ __kill_base = { + kill_weapon_assign, + kill_weapon_get, + kill_action_assign, + kill_action_get, + kill_how_often_assign, + kill_how_often_get, +}; + +struct kill * +kill_new(void) +{ + struct kill *tmp; + if ((tmp = malloc(sizeof(struct kill))) == NULL) { + event_warn("%s: malloc", __func__); + return (NULL); + } + tmp->base = &__kill_base; + + tmp->weapon_data = NULL; + tmp->weapon_set = 0; + + tmp->action_data = NULL; + tmp->action_set = 0; + + tmp->how_often_data = 0; + tmp->how_often_set = 0; + + return (tmp); +} + + + + +int +kill_weapon_assign(struct kill *msg, + const char * value) +{ + if (msg->weapon_data != NULL) + free(msg->weapon_data); + if ((msg->weapon_data = strdup(value)) == NULL) + return (-1); + msg->weapon_set = 1; + return (0); +} + +int +kill_action_assign(struct kill *msg, + const char * value) +{ + if (msg->action_data != NULL) + free(msg->action_data); + if ((msg->action_data = strdup(value)) == NULL) + return (-1); + msg->action_set = 1; + return (0); +} + +int +kill_how_often_assign(struct kill *msg, const uint32_t value) +{ + msg->how_often_set = 1; + msg->how_often_data = value; + return (0); +} + +int +kill_weapon_get(struct kill *msg, char * *value) +{ + if (msg->weapon_set != 1) + return (-1); + *value = msg->weapon_data; + return (0); +} + +int +kill_action_get(struct kill *msg, char * *value) +{ + if (msg->action_set != 1) + return (-1); + *value = msg->action_data; + return (0); +} + +int +kill_how_often_get(struct kill *msg, uint32_t *value) +{ + if (msg->how_often_set != 1) + return (-1); + *value = msg->how_often_data; + return (0); +} + +void +kill_clear(struct kill *tmp) +{ + if (tmp->weapon_set == 1) { + free (tmp->weapon_data); + tmp->weapon_data = NULL; + tmp->weapon_set = 0; + } + if (tmp->action_set == 1) { + free (tmp->action_data); + tmp->action_data = NULL; + tmp->action_set = 0; + } + tmp->how_often_set = 0; +} + +void +kill_free(struct kill *tmp) +{ + if (tmp->weapon_data != NULL) + free (tmp->weapon_data); + if (tmp->action_data != NULL) + free (tmp->action_data); + free(tmp); +} + +void +kill_marshal(struct evbuffer *evbuf, const struct kill *tmp){ + evtag_marshal_string(evbuf, KILL_WEAPON, tmp->weapon_data); + evtag_marshal_string(evbuf, KILL_ACTION, tmp->action_data); + if (tmp->how_often_set) { + evtag_marshal_int(evbuf, KILL_HOW_OFTEN, tmp->how_often_data); + } +} + +int +kill_unmarshal(struct kill *tmp, struct evbuffer *evbuf) +{ + uint32_t tag; + while (EVBUFFER_LENGTH(evbuf) > 0) { + if (evtag_peek(evbuf, &tag) == -1) + return (-1); + switch (tag) { + + case KILL_WEAPON: + + if (tmp->weapon_set) + return (-1); + if (evtag_unmarshal_string(evbuf, KILL_WEAPON, &tmp->weapon_data) == -1) { + event_warnx("%s: failed to unmarshal weapon", __func__); + return (-1); + } + tmp->weapon_set = 1; + break; + + case KILL_ACTION: + + if (tmp->action_set) + return (-1); + if (evtag_unmarshal_string(evbuf, KILL_ACTION, &tmp->action_data) == -1) { + event_warnx("%s: failed to unmarshal action", __func__); + return (-1); + } + tmp->action_set = 1; + break; + + case KILL_HOW_OFTEN: + + if (tmp->how_often_set) + return (-1); + if (evtag_unmarshal_int(evbuf, KILL_HOW_OFTEN, &tmp->how_often_data) == -1) { + event_warnx("%s: failed to unmarshal how_often", __func__); + return (-1); + } + tmp->how_often_set = 1; + break; + + default: + return -1; + } + } + + if (kill_complete(tmp) == -1) + return (-1); + return (0); +} + +int +kill_complete(struct kill *msg) +{ + if (!msg->weapon_set) + return (-1); + if (!msg->action_set) + return (-1); + return (0); +} + +int +evtag_unmarshal_kill(struct evbuffer *evbuf, uint32_t need_tag, struct kill *msg) +{ + uint32_t tag; + int res = -1; + + struct evbuffer *tmp = evbuffer_new(); + + if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) + goto error; + + if (kill_unmarshal(msg, tmp) == -1) + goto error; + + res = 0; + + error: + evbuffer_free(tmp); + return (res); +} + +void +evtag_marshal_kill(struct evbuffer *evbuf, uint32_t tag, const struct kill *msg) +{ + struct evbuffer *_buf = evbuffer_new(); + assert(_buf != NULL); + evbuffer_drain(_buf, -1); + kill_marshal(_buf, msg); + evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), EVBUFFER_LENGTH(_buf)); + evbuffer_free(_buf); +} + +/* + * Implementation of run + */ + +static struct run_access_ __run_base = { + run_how_assign, + run_how_get, + run_some_bytes_assign, + run_some_bytes_get, + run_fixed_bytes_assign, + run_fixed_bytes_get, +}; + +struct run * +run_new(void) +{ + struct run *tmp; + if ((tmp = malloc(sizeof(struct run))) == NULL) { + event_warn("%s: malloc", __func__); + return (NULL); + } + tmp->base = &__run_base; + + tmp->how_data = NULL; + tmp->how_set = 0; + + tmp->some_bytes_data = NULL; + tmp->some_bytes_length = 0; + tmp->some_bytes_set = 0; + + memset(tmp->fixed_bytes_data, 0, sizeof(tmp->fixed_bytes_data)); + tmp->fixed_bytes_set = 0; + + return (tmp); +} + + + + +int +run_how_assign(struct run *msg, + const char * value) +{ + if (msg->how_data != NULL) + free(msg->how_data); + if ((msg->how_data = strdup(value)) == NULL) + return (-1); + msg->how_set = 1; + return (0); +} + +int +run_some_bytes_assign(struct run *msg, const uint8_t * value, uint32_t len) +{ + if (msg->some_bytes_data != NULL) + free (msg->some_bytes_data); + msg->some_bytes_data = malloc(len); + if (msg->some_bytes_data == NULL) + return (-1); + msg->some_bytes_set = 1; + msg->some_bytes_length = len; + memcpy(msg->some_bytes_data, value, len); + return (0); +} + +int +run_fixed_bytes_assign(struct run *msg, const uint8_t *value) +{ + msg->fixed_bytes_set = 1; + memcpy(msg->fixed_bytes_data, value, 24); + return (0); +} + +int +run_how_get(struct run *msg, char * *value) +{ + if (msg->how_set != 1) + return (-1); + *value = msg->how_data; + return (0); +} + +int +run_some_bytes_get(struct run *msg, uint8_t * *value, uint32_t *plen) +{ + if (msg->some_bytes_set != 1) + return (-1); + *value = msg->some_bytes_data; + *plen = msg->some_bytes_length; + return (0); +} + +int +run_fixed_bytes_get(struct run *msg, uint8_t **value) +{ + if (msg->fixed_bytes_set != 1) + return (-1); + *value = msg->fixed_bytes_data; + return (0); +} + +void +run_clear(struct run *tmp) +{ + if (tmp->how_set == 1) { + free (tmp->how_data); + tmp->how_data = NULL; + tmp->how_set = 0; + } + if (tmp->some_bytes_set == 1) { + free (tmp->some_bytes_data); + tmp->some_bytes_data = NULL; + tmp->some_bytes_length = 0; + tmp->some_bytes_set = 0; + } + tmp->fixed_bytes_set = 0; + memset(tmp->fixed_bytes_data, 0, sizeof(tmp->fixed_bytes_data)); +} + +void +run_free(struct run *tmp) +{ + if (tmp->how_data != NULL) + free (tmp->how_data); + if (tmp->some_bytes_data != NULL) + free (tmp->some_bytes_data); + free(tmp); +} + +void +run_marshal(struct evbuffer *evbuf, const struct run *tmp){ + evtag_marshal_string(evbuf, RUN_HOW, tmp->how_data); + if (tmp->some_bytes_set) { + evtag_marshal(evbuf, RUN_SOME_BYTES, tmp->some_bytes_data, tmp->some_bytes_length); + } + evtag_marshal(evbuf, RUN_FIXED_BYTES, tmp->fixed_bytes_data, sizeof(tmp->fixed_bytes_data)); +} + +int +run_unmarshal(struct run *tmp, struct evbuffer *evbuf) +{ + uint32_t tag; + while (EVBUFFER_LENGTH(evbuf) > 0) { + if (evtag_peek(evbuf, &tag) == -1) + return (-1); + switch (tag) { + + case RUN_HOW: + + if (tmp->how_set) + return (-1); + if (evtag_unmarshal_string(evbuf, RUN_HOW, &tmp->how_data) == -1) { + event_warnx("%s: failed to unmarshal how", __func__); + return (-1); + } + tmp->how_set = 1; + break; + + case RUN_SOME_BYTES: + + if (tmp->some_bytes_set) + return (-1); + if (evtag_payload_length(evbuf, &tmp->some_bytes_length) == -1) + return (-1); + if (tmp->some_bytes_length > EVBUFFER_LENGTH(evbuf)) + return (-1); + if ((tmp->some_bytes_data = malloc(tmp->some_bytes_length)) == NULL) + return (-1); + if (evtag_unmarshal_fixed(evbuf, RUN_SOME_BYTES, tmp->some_bytes_data, tmp->some_bytes_length) == -1) { + event_warnx("%s: failed to unmarshal some_bytes", __func__); + return (-1); + } + tmp->some_bytes_set = 1; + break; + + case RUN_FIXED_BYTES: + + if (tmp->fixed_bytes_set) + return (-1); + if (evtag_unmarshal_fixed(evbuf, RUN_FIXED_BYTES, tmp->fixed_bytes_data, sizeof(tmp->fixed_bytes_data)) == -1) { + event_warnx("%s: failed to unmarshal fixed_bytes", __func__); + return (-1); + } + tmp->fixed_bytes_set = 1; + break; + + default: + return -1; + } + } + + if (run_complete(tmp) == -1) + return (-1); + return (0); +} + +int +run_complete(struct run *msg) +{ + if (!msg->how_set) + return (-1); + if (!msg->fixed_bytes_set) + return (-1); + return (0); +} + +int +evtag_unmarshal_run(struct evbuffer *evbuf, uint32_t need_tag, struct run *msg) +{ + uint32_t tag; + int res = -1; + + struct evbuffer *tmp = evbuffer_new(); + + if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) + goto error; + + if (run_unmarshal(msg, tmp) == -1) + goto error; + + res = 0; + + error: + evbuffer_free(tmp); + return (res); +} + +void +evtag_marshal_run(struct evbuffer *evbuf, uint32_t tag, const struct run *msg) +{ + struct evbuffer *_buf = evbuffer_new(); + assert(_buf != NULL); + evbuffer_drain(_buf, -1); + run_marshal(_buf, msg); + evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), EVBUFFER_LENGTH(_buf)); + evbuffer_free(_buf); +} + diff --git a/libevent/test/regress.gen.h b/libevent/test/regress.gen.h new file mode 100644 index 00000000000..09591f0584b --- /dev/null +++ b/libevent/test/regress.gen.h @@ -0,0 +1,183 @@ +/* + * Automatically generated from ./regress.rpc + */ + +#ifndef ___REGRESS_RPC_ +#define ___REGRESS_RPC_ + +#include +#ifdef _EVENT_HAVE_STDINT_H +#include +#endif +#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1) +#ifdef __GNUC__ +#define EVTAG_ASSIGN(msg, member, args...) (*(msg)->base->member##_assign)(msg, ## args) +#define EVTAG_GET(msg, member, args...) (*(msg)->base->member##_get)(msg, ## args) +#else +#define EVTAG_ASSIGN(msg, member, ...) (*(msg)->base->member##_assign)(msg, ## __VA_ARGS__) +#define EVTAG_GET(msg, member, ...) (*(msg)->base->member##_get)(msg, ## __VA_ARGS__) +#endif +#define EVTAG_ADD(msg, member) (*(msg)->base->member##_add)(msg) +#define EVTAG_LEN(msg, member) ((msg)->member##_length) + +struct msg; +struct kill; +struct run; + +/* Tag definition for msg */ +enum msg_ { + MSG_FROM_NAME=1, + MSG_TO_NAME=2, + MSG_ATTACK=3, + MSG_RUN=4, + MSG_MAX_TAGS +}; + +/* Structure declaration for msg */ +struct msg_access_ { + int (*from_name_assign)(struct msg *, const char *); + int (*from_name_get)(struct msg *, char * *); + int (*to_name_assign)(struct msg *, const char *); + int (*to_name_get)(struct msg *, char * *); + int (*attack_assign)(struct msg *, const struct kill*); + int (*attack_get)(struct msg *, struct kill* *); + int (*run_assign)(struct msg *, int, const struct run *); + int (*run_get)(struct msg *, int, struct run * *); + struct run * (*run_add)(struct msg *); +}; + +struct msg { + struct msg_access_ *base; + + char *from_name_data; + char *to_name_data; + struct kill* attack_data; + struct run **run_data; + int run_length; + int run_num_allocated; + + uint8_t from_name_set; + uint8_t to_name_set; + uint8_t attack_set; + uint8_t run_set; +}; + +struct msg *msg_new(void); +void msg_free(struct msg *); +void msg_clear(struct msg *); +void msg_marshal(struct evbuffer *, const struct msg *); +int msg_unmarshal(struct msg *, struct evbuffer *); +int msg_complete(struct msg *); +void evtag_marshal_msg(struct evbuffer *, uint32_t, + const struct msg *); +int evtag_unmarshal_msg(struct evbuffer *, uint32_t, + struct msg *); +int msg_from_name_assign(struct msg *, const char *); +int msg_from_name_get(struct msg *, char * *); +int msg_to_name_assign(struct msg *, const char *); +int msg_to_name_get(struct msg *, char * *); +int msg_attack_assign(struct msg *, const struct kill*); +int msg_attack_get(struct msg *, struct kill* *); +int msg_run_assign(struct msg *, int, const struct run *); +int msg_run_get(struct msg *, int, struct run * *); +struct run * msg_run_add(struct msg *); +/* --- msg done --- */ + +/* Tag definition for kill */ +enum kill_ { + KILL_WEAPON=65825, + KILL_ACTION=2, + KILL_HOW_OFTEN=3, + KILL_MAX_TAGS +}; + +/* Structure declaration for kill */ +struct kill_access_ { + int (*weapon_assign)(struct kill *, const char *); + int (*weapon_get)(struct kill *, char * *); + int (*action_assign)(struct kill *, const char *); + int (*action_get)(struct kill *, char * *); + int (*how_often_assign)(struct kill *, const uint32_t); + int (*how_often_get)(struct kill *, uint32_t *); +}; + +struct kill { + struct kill_access_ *base; + + char *weapon_data; + char *action_data; + uint32_t how_often_data; + + uint8_t weapon_set; + uint8_t action_set; + uint8_t how_often_set; +}; + +struct kill *kill_new(void); +void kill_free(struct kill *); +void kill_clear(struct kill *); +void kill_marshal(struct evbuffer *, const struct kill *); +int kill_unmarshal(struct kill *, struct evbuffer *); +int kill_complete(struct kill *); +void evtag_marshal_kill(struct evbuffer *, uint32_t, + const struct kill *); +int evtag_unmarshal_kill(struct evbuffer *, uint32_t, + struct kill *); +int kill_weapon_assign(struct kill *, const char *); +int kill_weapon_get(struct kill *, char * *); +int kill_action_assign(struct kill *, const char *); +int kill_action_get(struct kill *, char * *); +int kill_how_often_assign(struct kill *, const uint32_t); +int kill_how_often_get(struct kill *, uint32_t *); +/* --- kill done --- */ + +/* Tag definition for run */ +enum run_ { + RUN_HOW=1, + RUN_SOME_BYTES=2, + RUN_FIXED_BYTES=3, + RUN_MAX_TAGS +}; + +/* Structure declaration for run */ +struct run_access_ { + int (*how_assign)(struct run *, const char *); + int (*how_get)(struct run *, char * *); + int (*some_bytes_assign)(struct run *, const uint8_t *, uint32_t); + int (*some_bytes_get)(struct run *, uint8_t * *, uint32_t *); + int (*fixed_bytes_assign)(struct run *, const uint8_t *); + int (*fixed_bytes_get)(struct run *, uint8_t **); +}; + +struct run { + struct run_access_ *base; + + char *how_data; + uint8_t *some_bytes_data; + uint32_t some_bytes_length; + uint8_t fixed_bytes_data[24]; + + uint8_t how_set; + uint8_t some_bytes_set; + uint8_t fixed_bytes_set; +}; + +struct run *run_new(void); +void run_free(struct run *); +void run_clear(struct run *); +void run_marshal(struct evbuffer *, const struct run *); +int run_unmarshal(struct run *, struct evbuffer *); +int run_complete(struct run *); +void evtag_marshal_run(struct evbuffer *, uint32_t, + const struct run *); +int evtag_unmarshal_run(struct evbuffer *, uint32_t, + struct run *); +int run_how_assign(struct run *, const char *); +int run_how_get(struct run *, char * *); +int run_some_bytes_assign(struct run *, const uint8_t *, uint32_t); +int run_some_bytes_get(struct run *, uint8_t * *, uint32_t *); +int run_fixed_bytes_assign(struct run *, const uint8_t *); +int run_fixed_bytes_get(struct run *, uint8_t **); +/* --- run done --- */ + +#endif /* ___REGRESS_RPC_ */ diff --git a/libevent/test/regress.h b/libevent/test/regress.h new file mode 100644 index 00000000000..4060ff5c6ac --- /dev/null +++ b/libevent/test/regress.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000-2004 Niels Provos + * 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 _REGRESS_H_ +#define _REGRESS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void http_suite(void); +void http_basic_test(void); + +void rpc_suite(void); + +void dns_suite(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _REGRESS_H_ */ diff --git a/libevent/test/regress.rpc b/libevent/test/regress.rpc new file mode 100644 index 00000000000..65ca95de4cf --- /dev/null +++ b/libevent/test/regress.rpc @@ -0,0 +1,20 @@ +/* tests data packing and unpacking */ + +struct msg { + string from_name = 1; + string to_name = 2; + optional struct[kill] attack = 3; + array struct[run] run = 4; +} + +struct kill { + string weapon = 0x10121; + string action = 2; + optional int how_often = 3; +} + +struct run { + string how = 1; + optional bytes some_bytes = 2; + bytes fixed_bytes[24] = 3; +} diff --git a/libevent/test/regress_dns.c b/libevent/test/regress_dns.c new file mode 100644 index 00000000000..129cdad498f --- /dev/null +++ b/libevent/test/regress_dns.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2003-2006 Niels Provos + * 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 WIN32 +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#include +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#include +#include +#include +#include +#include + +#include "event.h" +#include "evdns.h" +#include "log.h" + +static int dns_ok = 0; +static int dns_err = 0; + +void dns_suite(void); + +static void +dns_gethostbyname_cb(int result, char type, int count, int ttl, + void *addresses, void *arg) +{ + dns_ok = dns_err = 0; + + if (result == DNS_ERR_TIMEOUT) { + fprintf(stdout, "[Timed out] "); + dns_err = result; + goto out; + } + + if (result != DNS_ERR_NONE) { + fprintf(stdout, "[Error code %d] ", result); + goto out; + } + + fprintf(stderr, "type: %d, count: %d, ttl: %d: ", type, count, ttl); + + switch (type) { + case DNS_IPv6_AAAA: { +#if defined(HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) + struct in6_addr *in6_addrs = addresses; + char buf[INET6_ADDRSTRLEN+1]; + int i; + /* a resolution that's not valid does not help */ + if (ttl < 0) + goto out; + for (i = 0; i < count; ++i) { + const char *b = inet_ntop(AF_INET6, &in6_addrs[i], buf,sizeof(buf)); + if (b) + fprintf(stderr, "%s ", b); + else + fprintf(stderr, "%s ", strerror(errno)); + } +#endif + break; + } + case DNS_IPv4_A: { + struct in_addr *in_addrs = addresses; + int i; + /* a resolution that's not valid does not help */ + if (ttl < 0) + goto out; + for (i = 0; i < count; ++i) + fprintf(stderr, "%s ", inet_ntoa(in_addrs[i])); + break; + } + case DNS_PTR: + /* may get at most one PTR */ + if (count != 1) + goto out; + + fprintf(stderr, "%s ", *(char **)addresses); + break; + default: + goto out; + } + + dns_ok = type; + +out: + event_loopexit(NULL); +} + +static void +dns_gethostbyname(void) +{ + fprintf(stdout, "Simple DNS resolve: "); + dns_ok = 0; + evdns_resolve_ipv4("www.monkey.org", 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + if (dns_ok == DNS_IPv4_A) { + fprintf(stdout, "OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } +} + +static void +dns_gethostbyname6(void) +{ + fprintf(stdout, "IPv6 DNS resolve: "); + dns_ok = 0; + evdns_resolve_ipv6("www.ietf.org", 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + if (dns_ok == DNS_IPv6_AAAA) { + fprintf(stdout, "OK\n"); + } else if (!dns_ok && dns_err == DNS_ERR_TIMEOUT) { + fprintf(stdout, "SKIPPED\n"); + } else { + fprintf(stdout, "FAILED (%d)\n", dns_ok); + exit(1); + } +} + +static void +dns_gethostbyaddr(void) +{ + struct in_addr in; + in.s_addr = htonl(0x7f000001ul); /* 127.0.0.1 */ + fprintf(stdout, "Simple reverse DNS resolve: "); + dns_ok = 0; + evdns_resolve_reverse(&in, 0, dns_gethostbyname_cb, NULL); + event_dispatch(); + + if (dns_ok == DNS_PTR) { + fprintf(stdout, "OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } +} + +static int n_server_responses = 0; + +static void +dns_server_request_cb(struct evdns_server_request *req, void *data) +{ + int i, r; + const char TEST_ARPA[] = "11.11.168.192.in-addr.arpa"; + for (i = 0; i < req->nquestions; ++i) { + struct in_addr ans; + ans.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ + if (req->questions[i]->type == EVDNS_TYPE_A && + req->questions[i]->dns_question_class == EVDNS_CLASS_INET && + !strcmp(req->questions[i]->name, "zz.example.com")) { + r = evdns_server_request_add_a_reply(req, "zz.example.com", + 1, &ans.s_addr, 12345); + if (r<0) + dns_ok = 0; + } else if (req->questions[i]->type == EVDNS_TYPE_AAAA && + req->questions[i]->dns_question_class == EVDNS_CLASS_INET && + !strcmp(req->questions[i]->name, "zz.example.com")) { + char addr6[17] = "abcdefghijklmnop"; + r = evdns_server_request_add_aaaa_reply(req, "zz.example.com", + 1, addr6, 123); + if (r<0) + dns_ok = 0; + } else if (req->questions[i]->type == EVDNS_TYPE_PTR && + req->questions[i]->dns_question_class == EVDNS_CLASS_INET && + !strcmp(req->questions[i]->name, TEST_ARPA)) { + r = evdns_server_request_add_ptr_reply(req, NULL, TEST_ARPA, + "ZZ.EXAMPLE.COM", 54321); + if (r<0) + dns_ok = 0; + } else { + fprintf(stdout, "Unexpected question %d %d \"%s\" ", + req->questions[i]->type, + req->questions[i]->dns_question_class, + req->questions[i]->name); + dns_ok = 0; + } + } + r = evdns_server_request_respond(req, 0); + if (r<0) { + fprintf(stdout, "Couldn't send reply. "); + dns_ok = 0; + } +} + +static void +dns_server_gethostbyname_cb(int result, char type, int count, int ttl, + void *addresses, void *arg) +{ + if (result != DNS_ERR_NONE) { + fprintf(stdout, "Unexpected result %d. ", result); + dns_ok = 0; + goto out; + } + if (count != 1) { + fprintf(stdout, "Unexpected answer count %d. ", count); + dns_ok = 0; + goto out; + } + switch (type) { + case DNS_IPv4_A: { + struct in_addr *in_addrs = addresses; + if (in_addrs[0].s_addr != htonl(0xc0a80b0bUL) || ttl != 12345) { + fprintf(stdout, "Bad IPv4 response \"%s\" %d. ", + inet_ntoa(in_addrs[0]), ttl); + dns_ok = 0; + goto out; + } + break; + } + case DNS_IPv6_AAAA: { +#if defined (HAVE_STRUCT_IN6_ADDR) && defined(HAVE_INET_NTOP) && defined(INET6_ADDRSTRLEN) + struct in6_addr *in6_addrs = addresses; + char buf[INET6_ADDRSTRLEN+1]; + if (memcmp(&in6_addrs[0].s6_addr, "abcdefghijklmnop", 16) + || ttl != 123) { + const char *b = inet_ntop(AF_INET6, &in6_addrs[0],buf,sizeof(buf)); + fprintf(stdout, "Bad IPv6 response \"%s\" %d. ", b, ttl); + dns_ok = 0; + goto out; + } +#endif + break; + } + case DNS_PTR: { + char **addrs = addresses; + if (strcmp(addrs[0], "ZZ.EXAMPLE.COM") || ttl != 54321) { + fprintf(stdout, "Bad PTR response \"%s\" %d. ", + addrs[0], ttl); + dns_ok = 0; + goto out; + } + break; + } + default: + fprintf(stdout, "Bad response type %d. ", type); + dns_ok = 0; + } + + out: + if (++n_server_responses == 3) { + event_loopexit(NULL); + } +} + +static void +dns_server(void) +{ + int sock; + struct sockaddr_in my_addr; + struct evdns_server_port *port; + struct in_addr resolve_addr; + + dns_ok = 1; + fprintf(stdout, "DNS server support: "); + + /* Add ourself as the only nameserver, and make sure we really are + * the only nameserver. */ + evdns_nameserver_ip_add("127.0.0.1:35353"); + if (evdns_count_nameservers() != 1) { + fprintf(stdout, "Couldn't set up.\n"); + exit(1); + } + + /* Now configure a nameserver port. */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + perror("socket"); + exit(1); + } +#ifdef WIN32 + { + u_long nonblocking = 1; + ioctlsocket(sock, FIONBIO, &nonblocking); + } +#else + fcntl(sock, F_SETFL, O_NONBLOCK); +#endif + memset(&my_addr, 0, sizeof(my_addr)); + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(35353); + my_addr.sin_addr.s_addr = htonl(0x7f000001UL); + if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) { + perror("bind"); + exit (1); + } + port = evdns_add_server_port(sock, 0, dns_server_request_cb, NULL); + + /* Send two queries. */ + evdns_resolve_ipv4("zz.example.com", DNS_QUERY_NO_SEARCH, + dns_server_gethostbyname_cb, NULL); + evdns_resolve_ipv6("zz.example.com", DNS_QUERY_NO_SEARCH, + dns_server_gethostbyname_cb, NULL); + resolve_addr.s_addr = htonl(0xc0a80b0bUL); /* 192.168.11.11 */ + evdns_resolve_reverse(&resolve_addr, 0, + dns_server_gethostbyname_cb, NULL); + + event_dispatch(); + + if (dns_ok) { + fprintf(stdout, "OK\n"); + } else { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + evdns_close_server_port(port); + evdns_shutdown(0); /* remove ourself as nameserver. */ +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +void +dns_suite(void) +{ + dns_server(); /* Do this before we call evdns_init. */ + + evdns_init(); + dns_gethostbyname(); + dns_gethostbyname6(); + dns_gethostbyaddr(); + + evdns_shutdown(0); +} diff --git a/libevent/test/regress_http.c b/libevent/test/regress_http.c new file mode 100644 index 00000000000..1e2a1eb062a --- /dev/null +++ b/libevent/test/regress_http.c @@ -0,0 +1,1476 @@ +/* + * Copyright (c) 2003-2006 Niels Provos + * 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 WIN32 +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +#include "event.h" +#include "evhttp.h" +#include "log.h" +#include "http-internal.h" + +extern int pair[]; +extern int test_ok; + +static struct evhttp *http; +/* set if a test needs to call loopexit on a base */ +static struct event_base *base; + +void http_suite(void); + +void http_basic_cb(struct evhttp_request *req, void *arg); +static void http_chunked_cb(struct evhttp_request *req, void *arg); +void http_post_cb(struct evhttp_request *req, void *arg); +void http_dispatcher_cb(struct evhttp_request *req, void *arg); +static void http_large_delay_cb(struct evhttp_request *req, void *arg); + +static struct evhttp * +http_setup(short *pport, struct event_base *base) +{ + int i; + struct evhttp *myhttp; + short port = -1; + + /* Try a few different ports */ + myhttp = evhttp_new(base); + for (i = 0; i < 50; ++i) { + if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) { + port = 8080 + i; + break; + } + } + + if (port == -1) + event_errx(1, "Could not start web server"); + + /* Register a callback for certain types of requests */ + evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); + evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL); + evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); + evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL); + evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); + + *pport = port; + return (myhttp); +} + +#ifndef NI_MAXSERV +#define NI_MAXSERV 1024 +#endif + +static int +http_connect(const char *address, u_short port) +{ + /* Stupid code for connecting */ +#ifdef WIN32 + struct hostent *he; + struct sockaddr_in sin; +#else + struct addrinfo ai, *aitop; + char strport[NI_MAXSERV]; +#endif + struct sockaddr *sa; + int slen; + int fd; + +#ifdef WIN32 + if (!(he = gethostbyname(address))) { + event_warn("gethostbyname"); + } + memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + slen = sizeof(struct sockaddr_in); + sa = (struct sockaddr*)&sin; +#else + memset(&ai, 0, sizeof (ai)); + ai.ai_family = AF_INET; + ai.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof (strport), "%d", port); + if (getaddrinfo(address, strport, &ai, &aitop) != 0) { + event_warn("getaddrinfo"); + return (-1); + } + sa = aitop->ai_addr; + slen = aitop->ai_addrlen; +#endif + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) + event_err(1, "socket failed"); + + if (connect(fd, sa, slen) == -1) + event_err(1, "connect failed"); + +#ifndef WIN32 + freeaddrinfo(aitop); +#endif + + return (fd); +} + +static void +http_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "This is funny"; + + event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); + + if (evbuffer_find(bev->input, + (const unsigned char*) what, strlen(what)) != NULL) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + enum message_read_status done; + + req->kind = EVHTTP_RESPONSE; + done = evhttp_parse_firstline(req, bev->input); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers(req, bev->input); + if (done != ALL_DATA_READ) + goto out; + + if (done == 1 && + evhttp_find_header(req->input_headers, + "Content-Type") != NULL) + test_ok++; + + out: + evhttp_request_free(req); + bufferevent_disable(bev, EV_READ); + if (base) + event_base_loopexit(base, NULL); + else + event_loopexit(NULL); + } +} + +static void +http_writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(bev->output) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_errorcb(struct bufferevent *bev, short what, void *arg) +{ + test_ok = -2; + event_loopexit(NULL); +} + +void +http_basic_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, "This is funny"); + + /* For multi-line headers test */ + { + const char *multi = + evhttp_find_header(req->input_headers,"X-multi"); + if (multi) { + if (strcmp("END", multi + strlen(multi) - 3) == 0) + test_ok++; + if (evhttp_find_header(req->input_headers, "X-Last")) + test_ok++; + } + } + + /* injecting a bad content-length */ + if (evhttp_find_header(req->input_headers, "X-Negative")) + evhttp_add_header(req->output_headers, + "Content-Length", "-100"); + + /* allow sending of an empty reply */ + evhttp_send_reply(req, HTTP_OK, "Everything is fine", + !empty ? evb : NULL); + + evbuffer_free(evb); +} + +static char const* const CHUNKS[] = { + "This is funny", + "but not hilarious.", + "bwv 1052" +}; + +struct chunk_req_state { + struct evhttp_request *req; + int i; +}; + +static void +http_chunked_trickle_cb(int fd, short events, void *arg) +{ + struct evbuffer *evb = evbuffer_new(); + struct chunk_req_state *state = arg; + struct timeval when = { 0, 0 }; + + evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); + evhttp_send_reply_chunk(state->req, evb); + evbuffer_free(evb); + + if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { + event_once(-1, EV_TIMEOUT, + http_chunked_trickle_cb, state, &when); + } else { + evhttp_send_reply_end(state->req); + free(state); + } +} + +static void +http_chunked_cb(struct evhttp_request *req, void *arg) +{ + struct timeval when = { 0, 0 }; + struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); + event_debug(("%s: called\n", __func__)); + + memset(state, 0, sizeof(struct chunk_req_state)); + state->req = req; + + /* generate a chunked reply */ + evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); + + /* but trickle it across several iterations to ensure we're not + * assuming it comes all at once */ + event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); +} + +static void +http_complete_write(int fd, short what, void *arg) +{ + struct bufferevent *bev = arg; + const char *http_request = "host\r\n" + "Connection: close\r\n" + "\r\n"; + bufferevent_write(bev, http_request, strlen(http_request)); +} + +static void +http_basic_test(void) +{ + struct timeval tv; + struct bufferevent *bev; + int fd; + const char *http_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing Basic HTTP Server: "); + + http = http_setup(&port, NULL); + + /* bind to a second socket */ + if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { + fprintf(stdout, "FAILED (bind)\n"); + exit(1); + } + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + + /* first half of the http request */ + http_request = + "GET /test HTTP/1.1\r\n" + "Host: some"; + + bufferevent_write(bev, http_request, strlen(http_request)); + timerclear(&tv); + tv.tv_usec = 10000; + event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); + + event_dispatch(); + + if (test_ok != 3) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* connect to the second port */ + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + fd = http_connect("127.0.0.1", port + 1); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_dispatch(); + + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + evhttp_free(http); + + if (test_ok != 5) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +static struct evhttp_connection *delayed_client; + +static void +http_delay_reply(int fd, short what, void *arg) +{ + struct evhttp_request *req = arg; + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); + + ++test_ok; +} + +static void +http_large_delay_cb(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + timerclear(&tv); + tv.tv_sec = 3; + + event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); + + /* here we close the client connection which will cause an EOF */ + evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); +} + +void http_request_done(struct evhttp_request *, void *); +void http_request_empty_done(struct evhttp_request *, void *); + +static void +http_connection_test(int persistent) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing Request Connection Pipeline %s: ", + persistent ? "(persistent)" : ""); + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* try to make another request over the same connection */ + test_ok = 0; + + req = evhttp_request_new(http_request_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* + * if our connections are not supposed to be persistent; request + * a close from the server. + */ + if (!persistent) + evhttp_add_header(req->output_headers, "Connection", "close"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + /* make another request: request empty reply */ + test_ok = 0; + + req = evhttp_request_new(http_request_empty_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Empty", "itis"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + evhttp_connection_free(evcon); + evhttp_free(http); + + fprintf(stdout, "OK\n"); +} + +void +http_request_done(struct evhttp_request *req, void *arg) +{ + const char *what = "This is funny"; + + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* test date header and content length */ + +void +http_request_empty_done(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Date") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + + if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), + "0")) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != 0) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* + * HTTP DISPATCHER test + */ + +void +http_dispatcher_cb(struct evhttp_request *req, void *arg) +{ + + struct evbuffer *evb = evbuffer_new(); + event_debug(("%s: called\n", __func__)); + evbuffer_add_printf(evb, "DISPATCHER_TEST"); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +static void +http_dispatcher_test_done(struct evhttp_request *req, void *arg) +{ + const char *what = "DISPATCHER_TEST"; + + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stderr, "FAILED (content type)\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { + fprintf(stderr, "FAILED (length %zu vs %zu)\n", + EVBUFFER_LENGTH(req->input_buffer), strlen(what)); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { + fprintf(stderr, "FAILED (data)\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_dispatcher_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Dispatcher: "); + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* also bind to local host */ + evhttp_connection_set_local_address(evcon, "127.0.0.1"); + + /* + * At this point, we want to schedule an HTTP GET request + * server using our make request method. + */ + + req = evhttp_request_new(http_dispatcher_test_done, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_connection_free(evcon); + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED: %d\n", test_ok); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +/* + * HTTP POST test. + */ + +void http_postrequest_done(struct evhttp_request *, void *); + +#define POST_DATA "Okay. Not really printf" + +static void +http_post_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing HTTP POST Request: "); + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(http_postrequest_done, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->output_buffer, POST_DATA); + + if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_connection_free(evcon); + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED: %d\n", test_ok); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +void +http_post_cb(struct evhttp_request *req, void *arg) +{ + struct evbuffer *evb; + event_debug(("%s: called\n", __func__)); + + /* Yes, we are expecting a post request */ + if (req->type != EVHTTP_REQ_POST) { + fprintf(stdout, "FAILED (post type)\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) { + fprintf(stdout, "FAILED (length: %zu vs %zu)\n", + EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA)); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA, + strlen(POST_DATA))) { + fprintf(stdout, "FAILED (data)\n"); + fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer)); + fprintf(stdout, "Want:%s\n", POST_DATA); + exit(1); + } + + evb = evbuffer_new(); + evbuffer_add_printf(evb, "This is funny"); + + evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); + + evbuffer_free(evb); +} + +void +http_postrequest_done(struct evhttp_request *req, void *arg) +{ + const char *what = "This is funny"; + + if (req == NULL) { + fprintf(stderr, "FAILED (timeout)\n"); + exit(1); + } + + if (req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { + fprintf(stderr, "FAILED (content type)\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { + fprintf(stderr, "FAILED (length %zu vs %zu)\n", + EVBUFFER_LENGTH(req->input_buffer), strlen(what)); + exit(1); + } + + if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { + fprintf(stderr, "FAILED (data)\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_failure_readcb(struct bufferevent *bev, void *arg) +{ + const char *what = "400 Bad Request"; + if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) { + test_ok = 2; + bufferevent_disable(bev, EV_READ); + event_loopexit(NULL); + } +} + +/* + * Testing that the HTTP server can deal with a malformed request. + */ +static void +http_failure_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing Bad HTTP Request: "); + + http = http_setup(&port, NULL); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_failure_readcb, http_writecb, + http_errorcb, NULL); + + http_request = "illegal request\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_dispatch(); + + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + evhttp_free(http); + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +static void +close_detect_done(struct evhttp_request *req, void *arg) +{ + struct timeval tv; + if (req == NULL || req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + + timerclear(&tv); + tv.tv_sec = 3; /* longer than the http time out */ + + event_loopexit(&tv); +} + +static void +close_detect_launch(int fd, short what, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct evhttp_request *req; + + req = evhttp_request_new(close_detect_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } +} + +static void +close_detect_cb(struct evhttp_request *req, void *arg) +{ + struct evhttp_connection *evcon = arg; + struct timeval tv; + + if (req != NULL && req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED\n"); + exit(1); + } + + timerclear(&tv); + tv.tv_sec = 3; /* longer than the http time out */ + + /* launch a new request on the persistent connection in 6 seconds */ + event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); +} + + +static void +http_close_detection(int with_delay) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing Connection Close Detection%s: ", + with_delay ? " (with delay)" : ""); + + http = http_setup(&port, NULL); + + /* 2 second timeout */ + evhttp_set_timeout(http, 2); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + delayed_client = evcon; + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(close_detect_cb, evcon); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, + req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* at this point, the http server should have no connection */ + if (TAILQ_FIRST(&http->connections) != NULL) { + fprintf(stdout, "FAILED (left connections)\n"); + exit(1); + } + + evhttp_connection_free(evcon); + evhttp_free(http); + + fprintf(stdout, "OK\n"); +} + +static void +http_highport_test(void) +{ + int i = -1; + struct evhttp *myhttp = NULL; + + fprintf(stdout, "Testing HTTP Server with high port: "); + + /* Try a few different ports */ + for (i = 0; i < 50; ++i) { + myhttp = evhttp_start("127.0.0.1", 65535 - i); + if (myhttp != NULL) { + fprintf(stdout, "OK\n"); + evhttp_free(myhttp); + return; + } + } + + fprintf(stdout, "FAILED\n"); + exit(1); +} + +static void +http_bad_header_test(void) +{ + struct evkeyvalq headers; + + fprintf(stdout, "Testing HTTP Header filtering: "); + + TAILQ_INIT(&headers); + + if (evhttp_add_header(&headers, "One", "Two") != 0) + goto fail; + + if (evhttp_add_header(&headers, "One\r", "Two") != -1) + goto fail; + if (evhttp_add_header(&headers, "One", "Two") != 0) + goto fail; + if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) + goto fail; + if (evhttp_add_header(&headers, "One\r", "Two") != -1) + goto fail; + if (evhttp_add_header(&headers, "One\n", "Two") != -1) + goto fail; + if (evhttp_add_header(&headers, "One", "Two\r") != -1) + goto fail; + if (evhttp_add_header(&headers, "One", "Two\n") != -1) + goto fail; + + evhttp_clear_headers(&headers); + + fprintf(stdout, "OK\n"); + return; +fail: + fprintf(stdout, "FAILED\n"); + exit(1); +} + +static int validate_header( + const struct evkeyvalq* headers, + const char *key, const char *value) +{ + const char *real_val = evhttp_find_header(headers, key); + if (real_val == NULL) + return (-1); + if (strcmp(real_val, value) != 0) + return (-1); + return (0); +} + +static void +http_parse_query_test(void) +{ + struct evkeyvalq headers; + + fprintf(stdout, "Testing HTTP query parsing: "); + + TAILQ_INIT(&headers); + + evhttp_parse_query("http://www.test.com/?q=test", &headers); + if (validate_header(&headers, "q", "test") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); + if (validate_header(&headers, "q", "test") != 0) + goto fail; + if (validate_header(&headers, "foo", "bar") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); + if (validate_header(&headers, "q", "test foo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); + if (validate_header(&headers, "q", "test\nfoo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); + if (validate_header(&headers, "q", "test\rfoo") != 0) + goto fail; + evhttp_clear_headers(&headers); + + fprintf(stdout, "OK\n"); + return; +fail: + fprintf(stdout, "FAILED\n"); + exit(1); +} + +static void +http_base_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Server Event Base: "); + + base = event_init(); + + /* + * create another bogus base - which is being used by all subsequen + * tests - yuck! + */ + event_init(); + + http = http_setup(&port, base); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + bufferevent_base_set(base, bev); + + http_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + event_base_dispatch(base); + + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + evhttp_free(http); + + event_base_free(base); + base = NULL; + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +/* + * the server is going to reply with chunked data. + */ + +static void +http_chunked_readcb(struct bufferevent *bev, void *arg) +{ + /* nothing here */ +} + +static void +http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) +{ + if (!test_ok) + goto out; + + test_ok = -1; + + if ((what & EVBUFFER_EOF) != 0) { + struct evhttp_request *req = evhttp_request_new(NULL, NULL); + const char *header; + enum message_read_status done; + + req->kind = EVHTTP_RESPONSE; + done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); + if (done != ALL_DATA_READ) + goto out; + + done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); + if (done != ALL_DATA_READ) + goto out; + + header = evhttp_find_header(req->input_headers, "Transfer-Encoding"); + if (header == NULL || strcmp(header, "chunked")) + goto out; + + header = evhttp_find_header(req->input_headers, "Connection"); + if (header == NULL || strcmp(header, "close")) + goto out; + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 13 chars */ + if (strcmp(header, "d")) + goto out; + free((char*)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "This is funny", 13)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 18 chars */ + if (strcmp(header, "12")) + goto out; + free((char *)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "but not hilarious.", 18)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 8 chars */ + if (strcmp(header, "8")) + goto out; + free((char *)header); + + if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), + "bwv 1052.", 8)) + goto out; + + evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); + + header = evbuffer_readline(EVBUFFER_INPUT(bev)); + if (header == NULL) + goto out; + /* 0 chars */ + if (strcmp(header, "0")) + goto out; + free((char *)header); + + test_ok = 2; + } + +out: + event_loopexit(NULL); +} + +static void +http_chunked_writecb(struct bufferevent *bev, void *arg) +{ + if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { + /* enable reading of the reply */ + bufferevent_enable(bev, EV_READ); + test_ok++; + } +} + +static void +http_chunked_request_done(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_OK) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (evhttp_find_header(req->input_headers, + "Transfer-Encoding") == NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), + "This is funnybut not hilarious.bwv 1052", + 13 + 18 + 8)) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_chunked_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_request; + short port = -1; + struct timeval tv_start, tv_end; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + int i; + + test_ok = 0; + fprintf(stdout, "Testing Chunked HTTP Reply: "); + + http = http_setup(&port, NULL); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, + http_chunked_readcb, http_chunked_writecb, + http_chunked_errorcb, NULL); + + http_request = + "GET /chunked HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "\r\n"; + + bufferevent_write(bev, http_request, strlen(http_request)); + + evutil_gettimeofday(&tv_start, NULL); + + event_dispatch(); + + evutil_gettimeofday(&tv_end, NULL); + evutil_timersub(&tv_end, &tv_start, &tv_end); + + if (tv_end.tv_sec >= 1) { + fprintf(stdout, "FAILED (time)\n"); + exit (1); + } + + + if (test_ok != 2) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* now try again with the regular connection object */ + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* make two requests to check the keepalive behavior */ + for (i = 0; i < 2; i++) { + test_ok = 0; + req = evhttp_request_new(http_chunked_request_done, NULL); + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_GET, "/chunked") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + } + + evhttp_connection_free(evcon); + evhttp_free(http); + + fprintf(stdout, "OK\n"); +} + +static void +http_multi_line_header_test(void) +{ + struct bufferevent *bev; + int fd; + const char *http_start_request; + short port = -1; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Server with multi line: "); + + http = http_setup(&port, NULL); + + fd = http_connect("127.0.0.1", port); + + /* Stupid thing to send a request */ + bev = bufferevent_new(fd, http_readcb, http_writecb, + http_errorcb, NULL); + + http_start_request = + "GET /test HTTP/1.1\r\n" + "Host: somehost\r\n" + "Connection: close\r\n" + "X-Multi: aaaaaaaa\r\n" + " a\r\n" + "\tEND\r\n" + "X-Last: last\r\n" + "\r\n"; + + bufferevent_write(bev, http_start_request, strlen(http_start_request)); + + event_dispatch(); + + bufferevent_free(bev); + EVUTIL_CLOSESOCKET(fd); + + evhttp_free(http); + + if (test_ok != 4) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +static void +http_request_bad(struct evhttp_request *req, void *arg) +{ + if (req != NULL) { + fprintf(stderr, "FAILED\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +static void +http_negative_content_length_test(void) +{ + short port = -1; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + test_ok = 0; + fprintf(stdout, "Testing HTTP Negative Content Length: "); + + http = http_setup(&port, NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule a request to the HTTP + * server using our make request method. + */ + + req = evhttp_request_new(http_request_bad, NULL); + + /* Cause the response to have a negative content-length */ + evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); + + /* We give ownership of the request to the connection */ + if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + event_dispatch(); + + evhttp_free(http); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); +} + +void +http_suite(void) +{ + http_base_test(); + http_bad_header_test(); + http_parse_query_test(); + http_basic_test(); + http_connection_test(0 /* not-persistent */); + http_connection_test(1 /* persistent */); + http_close_detection(0 /* with delay */); + http_close_detection(1 /* with delay */); + http_post_test(); + http_failure_test(); + http_highport_test(); + http_dispatcher_test(); + + http_multi_line_header_test(); + http_negative_content_length_test(); + + http_chunked_test(); +} diff --git a/libevent/test/regress_rpc.c b/libevent/test/regress_rpc.c new file mode 100644 index 00000000000..760934766a1 --- /dev/null +++ b/libevent/test/regress_rpc.c @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2003-2006 Niels Provos + * 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 WIN32 +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#ifndef WIN32 +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "evhttp.h" +#include "log.h" +#include "evrpc.h" + +#include "regress.gen.h" + +void rpc_suite(void); + +extern int test_ok; + +static struct evhttp * +http_setup(short *pport) +{ + int i; + struct evhttp *myhttp; + short port = -1; + + /* Try a few different ports */ + for (i = 0; i < 50; ++i) { + myhttp = evhttp_start("127.0.0.1", 8080 + i); + if (myhttp != NULL) { + port = 8080 + i; + break; + } + } + + if (port == -1) + event_errx(1, "Could not start web server"); + + *pport = port; + return (myhttp); +} + +EVRPC_HEADER(Message, msg, kill); +EVRPC_HEADER(NeverReply, msg, kill); + +EVRPC_GENERATE(Message, msg, kill); +EVRPC_GENERATE(NeverReply, msg, kill); + +static int need_input_hook = 0; +static int need_output_hook = 0; + +static void +MessageCb(EVRPC_STRUCT(Message)* rpc, void *arg) +{ + struct kill* kill_reply = rpc->reply; + + if (need_input_hook) { + struct evhttp_request* req = EVRPC_REQUEST_HTTP(rpc); + const char *header = evhttp_find_header( + req->input_headers, "X-Hook"); + assert(strcmp(header, "input") == 0); + } + + /* we just want to fill in some non-sense */ + EVTAG_ASSIGN(kill_reply, weapon, "dagger"); + EVTAG_ASSIGN(kill_reply, action, "wave around like an idiot"); + + /* no reply to the RPC */ + EVRPC_REQUEST_DONE(rpc); +} + +static EVRPC_STRUCT(NeverReply) *saved_rpc; + +static void +NeverReplyCb(EVRPC_STRUCT(NeverReply)* rpc, void *arg) +{ + test_ok += 1; + saved_rpc = rpc; +} + +static void +rpc_setup(struct evhttp **phttp, short *pport, struct evrpc_base **pbase) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + + http = http_setup(&port); + base = evrpc_init(http); + + EVRPC_REGISTER(base, Message, msg, kill, MessageCb, NULL); + EVRPC_REGISTER(base, NeverReply, msg, kill, NeverReplyCb, NULL); + + *phttp = http; + *pport = port; + *pbase = base; + + need_input_hook = 0; + need_output_hook = 0; +} + +static void +rpc_teardown(struct evrpc_base *base) +{ + assert(EVRPC_UNREGISTER(base, Message) == 0); + assert(EVRPC_UNREGISTER(base, NeverReply) == 0); + + evrpc_free(base); +} + +static void +rpc_postrequest_failure(struct evhttp_request *req, void *arg) +{ + if (req->response_code != HTTP_SERVUNAVAIL) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + test_ok = 1; + event_loopexit(NULL); +} + +/* + * Test a malformed payload submitted as an RPC + */ + +static void +rpc_basic_test(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + + fprintf(stdout, "Testing Basic RPC Support: "); + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_failure, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + evbuffer_add_printf(req->output_buffer, "Some Nonsense"); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + test_ok = 0; + + event_dispatch(); + + evhttp_connection_free(evcon); + + rpc_teardown(base); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + evhttp_free(http); +} + +static void +rpc_postrequest_done(struct evhttp_request *req, void *arg) +{ + struct kill* kill_reply = NULL; + + if (req->response_code != HTTP_OK) { + + fprintf(stderr, "FAILED (response code)\n"); + exit(1); + } + + kill_reply = kill_new(); + + if ((kill_unmarshal(kill_reply, req->input_buffer)) == -1) { + fprintf(stderr, "FAILED (unmarshal)\n"); + exit(1); + } + + kill_free(kill_reply); + + test_ok = 1; + event_loopexit(NULL); +} + +static void +rpc_basic_message(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evhttp_connection *evcon = NULL; + struct evhttp_request *req = NULL; + struct msg *msg; + + fprintf(stdout, "Testing Good RPC Post: "); + + rpc_setup(&http, &port, &base); + + evcon = evhttp_connection_new("127.0.0.1", port); + if (evcon == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* + * At this point, we want to schedule an HTTP POST request + * server using our make request method. + */ + + req = evhttp_request_new(rpc_postrequest_done, NULL); + if (req == NULL) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + /* Add the information that we care about */ + evhttp_add_header(req->output_headers, "Host", "somehost"); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + msg_marshal(req->output_buffer, msg); + msg_free(msg); + + if (evhttp_make_request(evcon, req, + EVHTTP_REQ_POST, + "/.rpc.Message") == -1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + test_ok = 0; + + event_dispatch(); + + evhttp_connection_free(evcon); + + rpc_teardown(base); + + if (test_ok != 1) { + fprintf(stdout, "FAILED\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + evhttp_free(http); +} + +static struct evrpc_pool * +rpc_pool_with_connection(short port) +{ + struct evhttp_connection *evcon; + struct evrpc_pool *pool; + + pool = evrpc_pool_new(NULL); + assert(pool != NULL); + + evcon = evhttp_connection_new("127.0.0.1", port); + assert(evcon != NULL); + + evrpc_pool_add_connection(pool, evcon); + + return (pool); +} + +static void +GotKillCb(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + char *weapon; + char *action; + + if (need_output_hook) { + struct evhttp_request *req = status->http_req; + const char *header = evhttp_find_header( + req->input_headers, "X-Pool-Hook"); + assert(strcmp(header, "ran") == 0); + } + + if (status->error != EVRPC_STATUS_ERR_NONE) + goto done; + + if (EVTAG_GET(kill, weapon, &weapon) == -1) { + fprintf(stderr, "get weapon\n"); + goto done; + } + if (EVTAG_GET(kill, action, &action) == -1) { + fprintf(stderr, "get action\n"); + goto done; + } + + if (strcmp(weapon, "dagger")) + goto done; + + if (strcmp(action, "wave around like an idiot")) + goto done; + + test_ok += 1; + +done: + event_loopexit(NULL); +} + +static void +GotKillCbTwo(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + char *weapon; + char *action; + + if (status->error != EVRPC_STATUS_ERR_NONE) + goto done; + + if (EVTAG_GET(kill, weapon, &weapon) == -1) { + fprintf(stderr, "get weapon\n"); + goto done; + } + if (EVTAG_GET(kill, action, &action) == -1) { + fprintf(stderr, "get action\n"); + goto done; + } + + if (strcmp(weapon, "dagger")) + goto done; + + if (strcmp(action, "wave around like an idiot")) + goto done; + + test_ok += 1; + +done: + if (test_ok == 2) + event_loopexit(NULL); +} + +static int +rpc_hook_add_header(struct evhttp_request *req, + struct evbuffer *evbuf, void *arg) +{ + const char *hook_type = arg; + if (strcmp("input", hook_type) == 0) + evhttp_add_header(req->input_headers, "X-Hook", hook_type); + else + evhttp_add_header(req->output_headers, "X-Hook", hook_type); + return (0); +} + +static int +rpc_hook_remove_header(struct evhttp_request *req, + struct evbuffer *evbuf, void *arg) +{ + const char *header = evhttp_find_header(req->input_headers, "X-Hook"); + assert(header != NULL); + assert(strcmp(header, arg) == 0); + evhttp_remove_header(req->input_headers, "X-Hook"); + evhttp_add_header(req->input_headers, "X-Pool-Hook", "ran"); + + return (0); +} + +static void +rpc_basic_client(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg; + struct kill *kill; + + fprintf(stdout, "Testing RPC Client: "); + + rpc_setup(&http, &port, &base); + + need_input_hook = 1; + need_output_hook = 1; + + assert(evrpc_add_hook(base, EVRPC_INPUT, rpc_hook_add_header, (void*)"input") + != NULL); + assert(evrpc_add_hook(base, EVRPC_OUTPUT, rpc_hook_add_header, (void*)"output") + != NULL); + + pool = rpc_pool_with_connection(port); + + assert(evrpc_add_hook(pool, EVRPC_INPUT, rpc_hook_remove_header, (void*)"output")); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill = kill_new(); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); + + test_ok = 0; + + event_dispatch(); + + if (test_ok != 1) { + fprintf(stdout, "FAILED (1)\n"); + exit(1); + } + + /* we do it twice to make sure that reuse works correctly */ + kill_clear(kill); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill, GotKillCb, NULL); + + event_dispatch(); + + rpc_teardown(base); + + if (test_ok != 2) { + fprintf(stdout, "FAILED (2)\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + msg_free(msg); + kill_free(kill); + + evrpc_pool_free(pool); + evhttp_free(http); +} + +/* + * We are testing that the second requests gets send over the same + * connection after the first RPCs completes. + */ +static void +rpc_basic_queued_client(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg; + struct kill *kill_one, *kill_two; + + fprintf(stdout, "Testing RPC (Queued) Client: "); + + rpc_setup(&http, &port, &base); + + pool = rpc_pool_with_connection(port); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill_one = kill_new(); + kill_two = kill_new(); + + EVRPC_MAKE_REQUEST(Message, pool, msg, kill_one, GotKillCbTwo, NULL); + EVRPC_MAKE_REQUEST(Message, pool, msg, kill_two, GotKillCb, NULL); + + test_ok = 0; + + event_dispatch(); + + rpc_teardown(base); + + if (test_ok != 2) { + fprintf(stdout, "FAILED (1)\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + msg_free(msg); + kill_free(kill_one); + kill_free(kill_two); + + evrpc_pool_free(pool); + evhttp_free(http); +} + +static void +GotErrorCb(struct evrpc_status *status, + struct msg *msg, struct kill *kill, void *arg) +{ + if (status->error != EVRPC_STATUS_ERR_TIMEOUT) + goto done; + + /* should never be complete but just to check */ + if (kill_complete(kill) == 0) + goto done; + + test_ok += 1; + +done: + event_loopexit(NULL); +} + +static void +rpc_client_timeout(void) +{ + short port; + struct evhttp *http = NULL; + struct evrpc_base *base = NULL; + struct evrpc_pool *pool = NULL; + struct msg *msg; + struct kill *kill; + + fprintf(stdout, "Testing RPC Client Timeout: "); + + rpc_setup(&http, &port, &base); + + pool = rpc_pool_with_connection(port); + + /* set the timeout to 5 seconds */ + evrpc_pool_set_timeout(pool, 5); + + /* set up the basic message */ + msg = msg_new(); + EVTAG_ASSIGN(msg, from_name, "niels"); + EVTAG_ASSIGN(msg, to_name, "tester"); + + kill = kill_new(); + + EVRPC_MAKE_REQUEST(NeverReply, pool, msg, kill, GotErrorCb, NULL); + + test_ok = 0; + + event_dispatch(); + + /* free the saved RPC structure up */ + EVRPC_REQUEST_DONE(saved_rpc); + + rpc_teardown(base); + + if (test_ok != 2) { + fprintf(stdout, "FAILED (1)\n"); + exit(1); + } + + fprintf(stdout, "OK\n"); + + msg_free(msg); + kill_free(kill); + + evrpc_pool_free(pool); + evhttp_free(http); +} + +void +rpc_suite(void) +{ + rpc_basic_test(); + rpc_basic_message(); + rpc_basic_client(); + rpc_basic_queued_client(); + rpc_client_timeout(); +} diff --git a/libevent/test/test-eof.c b/libevent/test/test-eof.c new file mode 100644 index 00000000000..4fc1a19f224 --- /dev/null +++ b/libevent/test/test-eof.c @@ -0,0 +1,82 @@ +/* + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef WIN32 +#include +#endif +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +int test_okay = 1; +int called = 0; + +static void +read_cb(int fd, short event, void *arg) +{ + char buf[256]; + int len; + + len = read(fd, buf, sizeof(buf)); + + printf("%s: read %d%s\n", __func__, + len, len ? "" : " - means EOF"); + + if (len) { + if (!called) + event_add(arg, NULL); + } else if (called == 1) + test_okay = 0; + + called++; +} + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +int +main (int argc, char **argv) +{ + struct event ev; + const char *test = "test string"; + int pair[2]; + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + + write(pair[0], test, strlen(test)+1); + shutdown(pair[0], SHUT_WR); + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + event_set(&ev, pair[1], EV_READ, read_cb, &ev); + + event_add(&ev, NULL); + + event_dispatch(); + + return (test_okay); +} + diff --git a/libevent/test/test-init.c b/libevent/test/test-init.c new file mode 100644 index 00000000000..c368715fd67 --- /dev/null +++ b/libevent/test/test-init.c @@ -0,0 +1,33 @@ +/* + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +int +main(int argc, char **argv) +{ + /* Initalize the event library */ + event_init(); + + return (0); +} + diff --git a/libevent/test/test-time.c b/libevent/test/test-time.c new file mode 100644 index 00000000000..a847d55ef38 --- /dev/null +++ b/libevent/test/test-time.c @@ -0,0 +1,82 @@ +/* + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int called = 0; + +#define NEVENT 20000 + +struct event *ev[NEVENT]; + +static int +rand_int(int n) +{ +#ifdef WIN32 + return (int)(rand() * n); +#else + return (int)(random() % n); +#endif +} + +static void +time_cb(int fd, short event, void *arg) +{ + struct timeval tv; + int i, j; + + called++; + + if (called < 10*NEVENT) { + for (i = 0; i < 10; i++) { + j = rand_int(NEVENT); + tv.tv_sec = 0; + tv.tv_usec = rand_int(50000); + if (tv.tv_usec % 2) + evtimer_add(ev[j], &tv); + else + evtimer_del(ev[j]); + } + } +} + +int +main (int argc, char **argv) +{ + struct timeval tv; + int i; + + /* Initalize the event library */ + event_init(); + + for (i = 0; i < NEVENT; i++) { + ev[i] = malloc(sizeof(struct event)); + + /* Initalize one event */ + evtimer_set(ev[i], time_cb, ev[i]); + tv.tv_sec = 0; + tv.tv_usec = rand_int(50000); + evtimer_add(ev[i], &tv); + } + + event_dispatch(); + + return (called < NEVENT); +} + diff --git a/libevent/test/test-weof.c b/libevent/test/test-weof.c new file mode 100644 index 00000000000..5d87ceb8eb7 --- /dev/null +++ b/libevent/test/test-weof.c @@ -0,0 +1,80 @@ +/* + * Compile with: + * cc -I/usr/local/include -o time-test time-test.c -L/usr/local/lib -levent + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef WIN32 +#include +#endif +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int pair[2]; +int test_okay = 1; +int called = 0; + +static void +write_cb(int fd, short event, void *arg) +{ + const char *test = "test string"; + int len; + + len = write(fd, test, strlen(test) + 1); + + printf("%s: write %d%s\n", __func__, + len, len ? "" : " - means EOF"); + + if (len > 0) { + if (!called) + event_add(arg, NULL); + close(pair[0]); + } else if (called == 1) + test_okay = 0; + + called++; +} + +int +main (int argc, char **argv) +{ + struct event ev; + +#ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + return (1); +#endif + + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + /* Initalize the event library */ + event_init(); + + /* Initalize one event */ + event_set(&ev, pair[1], EV_WRITE, write_cb, &ev); + + event_add(&ev, NULL); + + event_dispatch(); + + return (test_okay); +} + diff --git a/libevent/test/test.sh b/libevent/test/test.sh new file mode 100644 index 00000000000..506a1988c34 --- /dev/null +++ b/libevent/test/test.sh @@ -0,0 +1,91 @@ +#!/bin/sh + +setup () { + EVENT_NOKQUEUE=yes; export EVENT_NOKQUEUE + EVENT_NODEVPOLL=yes; export EVENT_NODEVPOLL + EVENT_NOPOLL=yes; export EVENT_NOPOLL + EVENT_NOSELECT=yes; export EVENT_NOSELECT + EVENT_NOEPOLL=yes; export EVENT_NOEPOLL + EVENT_NOEVPORT=yes; export EVENT_NOEVPORT +} + +test () { + if ./test-init 2>/dev/null ; + then + true + else + echo Skipping test + return + fi + +echo -n " test-eof: " +if ./test-eof >/dev/null ; +then + echo OKAY ; +else + echo FAILED ; +fi +echo -n " test-weof: " +if ./test-weof >/dev/null ; +then + echo OKAY ; +else + echo FAILED ; +fi +echo -n " test-time: " +if ./test-time >/dev/null ; +then + echo OKAY ; +else + echo FAILED ; +fi +echo -n " regress: " +if ./regress >/dev/null ; +then + echo OKAY ; +else + echo FAILED ; +fi +} + +echo "Running tests:" + +# Need to do this by hand? +setup +unset EVENT_NOKQUEUE +export EVENT_NOKQUEUE +echo "KQUEUE" +test + +setup +unset EVENT_NODEVPOLL +export EVENT_NODEVPOLL +echo "DEVPOLL" +test + +setup +unset EVENT_NOPOLL +export EVENT_NOPOLL +echo "POLL" +test + +setup +unset EVENT_NOSELECT +export EVENT_NOSELECT +echo "SELECT" +test + +setup +unset EVENT_NOEPOLL +export EVENT_NOEPOLL +echo "EPOLL" +test + +setup +unset EVENT_NOEVPORT +export EVENT_NOEVPORT +echo "EVPORT" +test + + + diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 1ddb0f7db98..b818ae58524 100644 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -334,7 +334,8 @@ SET(CLIENT_SOURCES ../sql-common/client.c ../sql-common/mysql_async.c ../sql-common/my_time.c - ../sql-common/client_plugin.c + ../sql-common/client_plugin.c + ../sql-common/client_authentication.cc ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c @@ -344,7 +345,7 @@ ADD_CONVENIENCE_LIBRARY(clientlib ${CLIENT_SOURCES}) DTRACE_INSTRUMENT(clientlib) ADD_DEPENDENCIES(clientlib GenError) -SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) +SET(LIBS clientlib dbug strings vio mysys mysys_ssl ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 4c4485f7ec4..9985fa2233c 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -85,6 +85,8 @@ const char *client_errors[]= "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", "This handle is already connected. Use a separate handle for each connection.", "Authentication plugin '%s' cannot be loaded: %s", + "There is an attribute with the same name already", + "Authentication plugin '%s' reported error: %s", "" }; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index fdc7ac98468..45c141649e1 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -1139,7 +1139,7 @@ void my_net_local_init(NET *net) my_net_set_read_timeout(net, CLIENT_NET_READ_TIMEOUT); my_net_set_write_timeout(net, CLIENT_NET_WRITE_TIMEOUT); net->retry_count= 1; - net->max_packet_size= max(net_buffer_length, max_allowed_packet); + net->max_packet_size= MY_MAX(net_buffer_length, max_allowed_packet); } /* @@ -3239,7 +3239,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, copy_length= end - start; /* We've got some data beyond offset: copy up to buffer_length bytes */ if (param->buffer_length) - memcpy(buffer, start, min(copy_length, param->buffer_length)); + memcpy(buffer, start, MY_MIN(copy_length, param->buffer_length)); } else copy_length= 0; @@ -3464,7 +3464,7 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, size_t len; if (field->decimals >= NOT_FIXED_DEC) len= my_gcvt(value, type, - (int) min(sizeof(buff)-1, param->buffer_length), + (int) MY_MIN(sizeof(buff)-1, param->buffer_length), buff, NULL); else len= my_fcvt(value, (int) field->decimals, buff, NULL); @@ -3774,7 +3774,7 @@ static void fetch_result_bin(MYSQL_BIND *param, uchar **row) { ulong length= net_field_length(row); - ulong copy_length= min(length, param->buffer_length); + ulong copy_length= MY_MIN(length, param->buffer_length); memcpy(param->buffer, (char *)*row, copy_length); *param->length= length; *param->error= copy_length < length; @@ -3786,7 +3786,7 @@ static void fetch_result_str(MYSQL_BIND *param, uchar **row) { ulong length= net_field_length(row); - ulong copy_length= min(length, param->buffer_length); + ulong copy_length= MY_MIN(length, param->buffer_length); memcpy(param->buffer, (char *)*row, copy_length); /* Add an end null if there is room in the buffer */ if (copy_length != param->buffer_length) diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index d4a57b19b8e..6f7a630663e 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -64,7 +64,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/sql_analyse.cc ../sql/sql_base.cc ../sql/sql_cache.cc ../sql/sql_class.cc ../sql/sql_crypt.cc ../sql/sql_cursor.cc ../sql/sql_db.cc ../sql/sql_delete.cc ../sql/sql_derived.cc - ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc + ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc + ../sql/sql_get_diagnostics.cc ../sql/sql_help.cc ../sql/sql_insert.cc ../sql/datadict.cc ../sql/sql_admin.cc ../sql/sql_truncate.cc ../sql/sql_reload.cc ../sql/sql_lex.cc ../sql/keycaches.cc @@ -120,7 +121,7 @@ ENDIF() SET(LIBS - dbug strings regex mysys vio + dbug strings regex mysys mysys_ssl vio ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${MYSQLD_STATIC_PLUGIN_LIBS} diff --git a/libmysqld/emb_qcache.cc b/libmysqld/emb_qcache.cc index abb0631ebfb..f1e850f4218 100644 --- a/libmysqld/emb_qcache.cc +++ b/libmysqld/emb_qcache.cc @@ -487,7 +487,7 @@ int emb_load_querycache_result(THD *thd, Querycache_stream *src) data->embedded_info->prev_ptr= prev_row; return_ok: net_send_eof(thd, thd->server_status, - thd->warning_info->statement_warn_count()); + thd->get_stmt_da()->current_statement_warn_count()); DBUG_RETURN(0); err: DBUG_RETURN(1); diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 7450dc22184..75188c38211 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -130,7 +130,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, /* Clear result variables */ thd->clear_error(); - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); mysql->affected_rows= ~(my_ulonglong) 0; mysql->field_count= 0; net_clear_error(net); @@ -241,7 +241,7 @@ static my_bool emb_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) stmt->stmt_id= thd->client_stmt_id; stmt->param_count= thd->client_param_count; stmt->field_count= 0; - mysql->warning_count= thd->warning_info->statement_warn_count(); + mysql->warning_count= thd->get_stmt_da()->current_statement_warn_count(); if (thd->first_data) { @@ -428,7 +428,7 @@ static void emb_free_embedded_thd(MYSQL *mysql) static const char * emb_read_statistics(MYSQL *mysql) { THD *thd= (THD*)mysql->thd; - return thd->is_error() ? thd->stmt_da->message() : ""; + return thd->is_error() ? thd->get_stmt_da()->message() : ""; } @@ -542,6 +542,10 @@ int init_embedded_server(int argc, char **argv, char **groups) system_charset_info= &my_charset_utf8_general_ci; sys_var_init(); + int ho_error= handle_early_options(); + if (ho_error != 0) + return 1; + if (init_common_variables()) { mysql_server_end(); @@ -885,7 +889,7 @@ write_eof_packet(THD *thd, uint server_status, uint statement_warn_count) is cleared between substatements, and mysqltest gets confused */ thd->cur_data->embedded_info->warning_count= - (thd->spcont ? 0 : min(statement_warn_count, 65535)); + (thd->spcont ? 0 : MY_MIN(statement_warn_count, 65535)); return FALSE; } @@ -1045,7 +1049,7 @@ bool Protocol::send_result_set_metadata(List *list, uint flags) if (flags & SEND_EOF) write_eof_packet(thd, thd->server_status, - thd->warning_info->statement_warn_count()); + thd->get_stmt_da()->current_statement_warn_count()); DBUG_RETURN(prepare_for_send(list->elements)); err: diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt index e7dcf20e547..c6e077b0a03 100644 --- a/libservices/CMakeLists.txt +++ b/libservices/CMakeLists.txt @@ -22,6 +22,7 @@ SET(MYSQLSERVICES_SOURCES thd_timezone_service.c progress_report_service.c debug_sync_service.c + my_sha1_service.c kill_statement_service.c) ADD_CONVENIENCE_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES}) diff --git a/libservices/my_sha1_service.c b/libservices/my_sha1_service.c new file mode 100644 index 00000000000..196c1939082 --- /dev/null +++ b/libservices/my_sha1_service.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2013 Monty Program Ab + Use is subject to license terms. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include +SERVICE_VERSION my_sha1_service= (void*)VERSION_my_sha1; diff --git a/mysql-test/include/commit.inc b/mysql-test/include/commit.inc index fdb9ef1f563..bdb6f48f095 100644 --- a/mysql-test/include/commit.inc +++ b/mysql-test/include/commit.inc @@ -7,7 +7,7 @@ ## set sql_mode=no_engine_substitution; -eval set storage_engine = $engine_type; +eval set default_storage_engine = $engine_type; set autocommit=1; --disable_warnings @@ -757,7 +757,7 @@ call p_verify_status_increment(2, 0, 4, 4); alter table t3 add column (b int); call p_verify_status_increment(2, 0, 2, 0); alter table t3 rename t4; -call p_verify_status_increment(2, 0, 2, 0); +call p_verify_status_increment(0, 0, 0, 0); rename table t4 to t3; call p_verify_status_increment(0, 0, 0, 0); truncate table t3; diff --git a/mysql-test/include/default_mysqld.cnf b/mysql-test/include/default_mysqld.cnf index 1c21a4f03cc..5f93428e599 100644 --- a/mysql-test/include/default_mysqld.cnf +++ b/mysql-test/include/default_mysqld.cnf @@ -1,4 +1,4 @@ -# Copyright (c) 2007, 2010, Oracle and/or its affiliates +# Copyright (c) 2007, 2012, Oracle and/or its affiliates # Copyright (c) 2010, 2012, Monty Program Ab # # This program is free software; you can redistribute it and/or modify @@ -48,21 +48,40 @@ loose-innodb_read_io_threads= 2 loose-innodb_log_buffer_size= 1M loose-innodb_log_file_size= 5M loose-innodb_log_files_in_group= 2 +loose-innodb-stats-persistent= OFF slave-net-timeout=120 +# MAINTAINER: +# the loose- syntax is to make sure the cnf file is also +# valid when building without the performance schema. + # Run tests with the performance schema instrumentation loose-enable-performance-schema # Run tests with a small number of instrumented objects # to limit memory consumption with MTR +loose-performance-schema-accounts-size=100 +loose-performance-schema-digests-size=200 +loose-performance-schema-hosts-size=100 +loose-performance-schema-users-size=100 loose-performance-schema-max-mutex-instances=5000 loose-performance-schema-max-rwlock-instances=5000 +loose-performance-schema-max-cond-instances=1000 +loose-performance-schema-max-file-instances=10000 +loose-performance-schema-max-socket-instances=1000 loose-performance-schema-max-table-instances=500 loose-performance-schema-max-table-handles=1000 +loose-performance-schema-events-waits-history-size=10 +loose-performance-schema-events-waits-history-long-size=10000 +loose-performance-schema-events-stages-history-size=10 loose-performance-schema-events-stages-history-long-size=1000 +loose-performance-schema-events-statements-history-size=10 loose-performance-schema-events-statements-history-long-size=1000 loose-performance-schema-max-thread-instances=200 +loose-performance-schema-session-connect-attrs-size=2048 + +# Enable everything, for maximun code exposure during testing loose-performance-schema-instrument='%=ON' diff --git a/mysql-test/include/have_innodb.combinations b/mysql-test/include/have_innodb.combinations index 55107204097..b76f783b928 100644 --- a/mysql-test/include/have_innodb.combinations +++ b/mysql-test/include/have_innodb.combinations @@ -7,6 +7,8 @@ innodb-trx innodb-buffer-pool-stats innodb-buffer-page innodb-buffer-page-lru +innodb-sys-foreign +innodb-sys-foreign-col [xtradb_plugin] ignore-builtin-innodb @@ -17,6 +19,8 @@ innodb-trx innodb-buffer-pool-stats innodb-buffer-page innodb-buffer-page-lru +innodb-sys-foreign +innodb-sys-foreign-col [xtradb] innodb @@ -26,3 +30,5 @@ innodb-metrics innodb-buffer-pool-stats innodb-buffer-page innodb-buffer-page-lru +innodb-sys-foreign +innodb-sys-foreign-col diff --git a/mysql-test/include/have_ipv6.inc b/mysql-test/include/have_ipv6.inc new file mode 100644 index 00000000000..752dd0db53e --- /dev/null +++ b/mysql-test/include/have_ipv6.inc @@ -0,0 +1,20 @@ +# Check if ipv6 is available. +# +--disable_query_log +--disable_result_log +--disable_abort_on_error +connect (checkcon123456789,::1,root,,test); +if($mysql_errno) +{ + skip No IPv6 support; +} +connection default; +if(!$mysql_errno) +{ + disconnect checkcon123456789; +} +--enable_abort_on_error +--enable_result_log +--enable_query_log +# end check + diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 75ba0e43221..0a5b63b4280 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -25,7 +25,7 @@ # where just some indexes have been created must be used. # -eval SET SESSION STORAGE_ENGINE = $engine_type; +eval SET SESSION DEFAULT_STORAGE_ENGINE = $engine_type; --disable_warnings drop table if exists t1,t2,t3,t1m,t1i,t2m,t2i,t4; @@ -388,7 +388,7 @@ drop table t1; # Bug #13293 Wrongly used index results in endless loop. # (was part of group_min_max.test) # -create table t1 (f1 int, f2 char(1), primary key(f1,f2)); +create table t1 (f1 int, f2 char(1), primary key(f1,f2)) stats_persistent=0; insert into t1 values ( 1,"e"),(2,"a"),( 3,"c"),(4,"d"); alter table t1 drop primary key, add primary key (f2, f1); explain select distinct f1 a, f1 b from t1; @@ -432,7 +432,7 @@ CREATE TABLE t1 ( age tinyint(3) unsigned NOT NULL, PRIMARY KEY (id), INDEX (name,dept) -) ENGINE=InnoDB; +) ENGINE=InnoDB STATS_PERSISTENT=0; INSERT INTO t1(id, dept, age, name) VALUES (3987, 'cs1', 10, 'rs1'), (3988, 'cs2', 20, 'rs1'), (3995, 'cs3', 10, 'rs2'), (3996, 'cs4', 20, 'rs2'), (4003, 'cs5', 10, 'rs3'), (4004, 'cs6', 20, 'rs3'), @@ -546,7 +546,7 @@ CREATE TABLE t2( acct_id int DEFAULT NULL, INDEX idx1 (stat_id, acct_id), INDEX idx2 (acct_id) -) ENGINE=InnoDB; +) ENGINE=InnoDB STATS_PERSISTENT=0; INSERT INTO t1(stat_id,acct_id) VALUES (1,759), (2,831), (3,785), (4,854), (1,921), @@ -631,8 +631,10 @@ copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/bug29807.frm; select * from bug29807; drop table t1; drop table bug29807; +--disable_query_log call mtr.add_suppression("InnoDB: Error: table .test...bug29807. does not exist in the InnoDB internal"); -call mtr.add_suppression("Cannot find or open table test\/bug29807 from"); +call mtr.add_suppression("InnoDB: Cannot open table test\/bug29807 from"); +--enable_query_log # @@ -1579,6 +1581,29 @@ DROP TABLE t1; --echo End of 5.1 tests +--echo # +--echo # Bug#43600: Incorrect type conversion caused wrong result. +--echo # +CREATE TABLE t1 ( + a int NOT NULL +) engine= innodb; + +CREATE TABLE t2 ( + a int NOT NULL, + b int NOT NULL, + filler char(100) DEFAULT NULL, + KEY a (a,b) +) engine= innodb; + +insert into t1 values (0),(1),(2),(3),(4); +insert into t2 select A.a + 10 *B.a, 1, 'filler' from t1 A, t1 B; + +explain select * from t1, t2 where t2.a=t1.a and t2.b + 1; +select * from t1, t2 where t2.a=t1.a and t2.b + 1; + +drop table t1,t2; +--echo # End of test case for the bug#43600 + --echo # --echo # Bug#42643: InnoDB does not support replication of TRUNCATE TABLE --echo # diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ea5b556fbaf..2cfa9a7e066 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3495,6 +3495,7 @@ sub mysql_install_db { mtr_add_arg($args, "--skip-plugin-$_") for @optional_plugins; # starting from 10.0 bootstrap scripts require InnoDB mtr_add_arg($args, "--loose-innodb"); + mtr_add_arg($args, "--loose-innodb-log-file-size=5M"); mtr_add_arg($args, "--disable-sync-frm"); mtr_add_arg($args, "--tmpdir=%s", "$opt_vardir/tmp/"); mtr_add_arg($args, "--core-file"); diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result index 7e4ab09b09d..cb82cb5fe7d 100644 --- a/mysql-test/r/1st.result +++ b/mysql-test/r/1st.result @@ -22,15 +22,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index b6e99952c23..c16f68dda3f 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -270,8 +270,8 @@ ERROR 42000: Incorrect table name '' drop table t1; drop table if exists t1, t2; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' create table t1 ( a varchar(10) not null primary key ) engine=myisam; create table t2 ( a varchar(10) not null primary key ) engine=merge union=(t1); flush tables; @@ -556,7 +556,7 @@ create database mysqltest; create table t1 (c1 int); alter table t1 rename mysqltest.t1; drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' alter table mysqltest.t1 rename t1; drop table t1; create table t1 (c1 int); @@ -978,7 +978,7 @@ SHOW CREATE TABLE `tt+2`; Table Create Table tt+2 CREATE TEMPORARY TABLE `tt+2` ( `c1` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE `tt+1`, `tt+2`; CREATE TABLE `#sql1` (c1 INT); CREATE TABLE `@0023sql2` (c1 INT); @@ -1015,12 +1015,12 @@ SHOW CREATE TABLE `#sql2`; Table Create Table #sql2 CREATE TEMPORARY TABLE `#sql2` ( `c1` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 SHOW CREATE TABLE `@0023sql1`; Table Create Table @0023sql1 CREATE TEMPORARY TABLE `@0023sql1` ( `c1` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE `#sql2`, `@0023sql1`; DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; @@ -1327,6 +1327,16 @@ CREATE DATABASE db1 CHARACTER SET utf8; CREATE TABLE db1.t1 (bar TINYTEXT, KEY (bar(100))); ALTER TABLE db1.t1 ADD baz INT; DROP DATABASE db1; +# Additional coverage for refactoring which is made as part +# of fix for bug #27480 "Extend CREATE TEMPORARY TABLES privilege +# to allow temp table operations". +# +# At some point the below test case failed on assertion. +DROP TABLE IF EXISTS t1; +CREATE TEMPORARY TABLE t1 (i int) ENGINE=MyISAM; +ALTER TABLE t1 DISCARD TABLESPACE; +ERROR HY000: Storage engine MyISAM of the table `test`.`t1` doesn't have this option +DROP TABLE t1; # # Bug#11938039 RE-EXECUTION OF FRM-ONLY ALTER TABLE WITH RENAME # CLAUSE FAILS OR ABORTS SERVER. @@ -1380,3 +1390,480 @@ t1 CREATE TABLE `t1` ( KEY `x_param1` (`x_param`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; +# +# Bug#11938817 ALTER BEHAVIOR DIFFERENT THEN DOCUMENTED +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT) engine=innodb; +INSERT INTO t1 VALUES (1), (2); +# This should not do anything +ALTER TABLE t1; +affected rows: 0 +# Check that we rebuild the table +ALTER TABLE t1 engine=innodb; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +# This should also rebuild the table +ALTER TABLE t1 FORCE; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +DROP TABLE t1; +# Bug#11748057 (formerly known as 34972): ALTER TABLE statement doesn't +# identify correct column name. +# +CREATE TABLE t1 (c1 int unsigned , c2 char(100) not null default ''); +ALTER TABLE t1 ADD c3 char(16) NOT NULL DEFAULT '' AFTER c2, +MODIFY c2 char(100) NOT NULL DEFAULT '' AFTER c1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(10) unsigned DEFAULT NULL, + `c2` char(100) NOT NULL DEFAULT '', + `c3` char(16) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +# +# WL#5534 Online ALTER, Phase 1 +# +# Single thread tests. +# See innodb_mysql_sync.test for multi thread tests. +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT PRIMARY KEY, b INT) engine=InnoDB; +CREATE TABLE m1(a INT PRIMARY KEY, b INT) engine=MyISAM; +INSERT INTO t1 VALUES (1,1), (2,2); +INSERT INTO m1 VALUES (1,1), (2,2); +# +# 1: Test ALGORITHM keyword +# +# --enable_info allows us to see how many rows were updated +# by ALTER TABLE. in-place will show 0 rows, while copy > 0. +ALTER TABLE t1 ADD INDEX i1(b); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i2(b), ALGORITHM= DEFAULT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i3(b), ALGORITHM= COPY; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i4(b), ALGORITHM= INPLACE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i5(b), ALGORITHM= INVALID; +ERROR HY000: Unknown ALGORITHM 'INVALID' +ALTER TABLE m1 ENABLE KEYS; +affected rows: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= DEFAULT; +affected rows: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= COPY; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= INPLACE; +affected rows: 0 +ALTER TABLE t1 DROP INDEX i1, DROP INDEX i2, DROP INDEX i3, DROP INDEX i4; +# +# 2: Test ALGORITHM + old_alter_table +# +SET SESSION old_alter_table= 1; +affected rows: 0 +ALTER TABLE t1 ADD INDEX i1(b); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i2(b), ALGORITHM= DEFAULT; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i3(b), ALGORITHM= COPY; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i4(b), ALGORITHM= INPLACE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +SET SESSION old_alter_table= 0; +affected rows: 0 +ALTER TABLE t1 DROP INDEX i1, DROP INDEX i2, DROP INDEX i3, DROP INDEX i4; +# +# 3: Test unsupported in-place operation +# +ALTER TABLE t1 ADD COLUMN (c1 INT); +ALTER TABLE t1 ADD COLUMN (c2 INT), ALGORITHM= DEFAULT; +ALTER TABLE t1 ADD COLUMN (c3 INT), ALGORITHM= COPY; +ALTER TABLE t1 ADD COLUMN (c4 INT), ALGORITHM= INPLACE; +ALTER TABLE t1 DROP COLUMN c1, DROP COLUMN c2, DROP COLUMN c3, DROP COLUMN c4; +# +# 4: Test LOCK keyword +# +ALTER TABLE t1 ADD INDEX i1(b), LOCK= DEFAULT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i2(b), LOCK= NONE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i3(b), LOCK= SHARED; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i4(b), LOCK= EXCLUSIVE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i5(b), LOCK= INVALID; +ERROR HY000: Unknown LOCK type 'INVALID' +ALTER TABLE m1 ENABLE KEYS, LOCK= DEFAULT; +ALTER TABLE m1 ENABLE KEYS, LOCK= NONE; +ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. +ALTER TABLE m1 ENABLE KEYS, LOCK= SHARED; +ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. +ALTER TABLE m1 ENABLE KEYS, LOCK= EXCLUSIVE; +ALTER TABLE t1 DROP INDEX i1, DROP INDEX i2, DROP INDEX i3, DROP INDEX i4; +# +# 5: Test ALGORITHM + LOCK +# +ALTER TABLE t1 ADD INDEX i1(b), ALGORITHM= INPLACE, LOCK= NONE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i2(b), ALGORITHM= INPLACE, LOCK= SHARED; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i3(b), ALGORITHM= INPLACE, LOCK= EXCLUSIVE; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i4(b), ALGORITHM= COPY, LOCK= NONE; +ERROR 0A000: LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED. +ALTER TABLE t1 ADD INDEX i5(b), ALGORITHM= COPY, LOCK= SHARED; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE t1 ADD INDEX i6(b), ALGORITHM= COPY, LOCK= EXCLUSIVE; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= INPLACE, LOCK= NONE; +ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= INPLACE, LOCK= SHARED; +ERROR 0A000: LOCK=NONE/SHARED is not supported for this operation. Try LOCK=EXCLUSIVE. +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= INPLACE, LOCK= EXCLUSIVE; +affected rows: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= COPY, LOCK= NONE; +ERROR 0A000: LOCK=NONE is not supported. Reason: COPY algorithm requires a lock. Try LOCK=SHARED. +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= COPY, LOCK= SHARED; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE m1 ENABLE KEYS, ALGORITHM= COPY, LOCK= EXCLUSIVE; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +DROP TABLE t1, m1; +# +# 6: Possible deadlock involving thr_lock.c +# +CREATE TABLE t1(a INT PRIMARY KEY, b INT); +INSERT INTO t1 VALUES (1,1), (2,2); +START TRANSACTION; +INSERT INTO t1 VALUES (3,3); +# Connection con1 +# Sending: +ALTER TABLE t1 DISABLE KEYS; +# Connection default +# Waiting until ALTER TABLE is blocked. +UPDATE t1 SET b = 4; +COMMIT; +# Connection con1 +# Reaping: ALTER TABLE t1 DISABLE KEYS +# Connection default +DROP TABLE t1; +# +# 7: Which operations require copy and which can be done in-place? +# +# Test which ALTER TABLE operations are done in-place and +# which operations are done using temporary table copy. +# +# --enable_info allows us to see how many rows were updated +# by ALTER TABLE. in-place will show 0 rows, while copy > 0. +# +DROP TABLE IF EXISTS ti1, ti2, ti3, tm1, tm2, tm3; +# Single operation tests +CREATE TABLE ti1(a INT NOT NULL, b INT, c INT) engine=InnoDB; +CREATE TABLE tm1(a INT NOT NULL, b INT, c INT) engine=MyISAM; +CREATE TABLE ti2(a INT PRIMARY KEY AUTO_INCREMENT, b INT, c INT) engine=InnoDB; +CREATE TABLE tm2(a INT PRIMARY KEY AUTO_INCREMENT, b INT, c INT) engine=MyISAM; +INSERT INTO ti1 VALUES (1,1,1), (2,2,2); +INSERT INTO ti2 VALUES (1,1,1), (2,2,2); +INSERT INTO tm1 VALUES (1,1,1), (2,2,2); +INSERT INTO tm2 VALUES (1,1,1), (2,2,2); +ALTER TABLE ti1; +affected rows: 0 +ALTER TABLE tm1; +affected rows: 0 +ALTER TABLE ti1 ADD COLUMN d VARCHAR(200); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD COLUMN d VARCHAR(200); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD COLUMN d2 VARCHAR(200); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD COLUMN d2 VARCHAR(200); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD COLUMN e ENUM('a', 'b') FIRST; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD COLUMN e ENUM('a', 'b') FIRST; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD COLUMN f INT AFTER a; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD COLUMN f INT AFTER a; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD INDEX ii1(b); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD INDEX im1(b); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD UNIQUE INDEX ii2 (c); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD UNIQUE INDEX im2 (c); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD FULLTEXT INDEX ii3 (d); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 1 +Warnings: +Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID +ALTER TABLE tm1 ADD FULLTEXT INDEX im3 (d); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD FULLTEXT INDEX ii4 (d2); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD FULLTEXT INDEX im4 (d2); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD PRIMARY KEY(a), ALGORITHM=INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY. +ALTER TABLE ti1 ADD PRIMARY KEY(a); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD PRIMARY KEY(a); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP INDEX ii3; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP INDEX im3; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP COLUMN d2; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP COLUMN d2; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ADD CONSTRAINT fi1 FOREIGN KEY (b) REFERENCES ti2(a); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ADD CONSTRAINT fm1 FOREIGN KEY (b) REFERENCES tm2(a); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ALTER COLUMN b SET DEFAULT 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ALTER COLUMN b SET DEFAULT 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 ALTER COLUMN b DROP DEFAULT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ALTER COLUMN b DROP DEFAULT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 CHANGE COLUMN f g INT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 CHANGE COLUMN f g INT; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 CHANGE COLUMN g h VARCHAR(20); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 CHANGE COLUMN g h VARCHAR(20); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN e ENUM('a', 'b', 'c'); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN e ENUM('a', 'b', 'c'); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN e INT; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN e INT; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN e INT AFTER h; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN e INT AFTER h; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN e INT FIRST; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN e INT FIRST; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +SET @orig_sql_mode = @@sql_mode; +SET @@sql_mode = 'STRICT_TRANS_TABLES'; +ALTER TABLE ti1 MODIFY COLUMN c INT NOT NULL; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +SET @@sql_mode = @orig_sql_mode; +ALTER TABLE tm1 MODIFY COLUMN c INT NOT NULL; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN c INT NULL; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN c INT NULL; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN h VARCHAR(30); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN h VARCHAR(30); +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MODIFY COLUMN h VARCHAR(30) AFTER d; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MODIFY COLUMN h VARCHAR(30) AFTER d; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP COLUMN h; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP COLUMN h; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP INDEX ii2; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP INDEX im2; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP PRIMARY KEY; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP PRIMARY KEY; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DROP FOREIGN KEY fi1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DROP FOREIGN KEY fm1; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 RENAME TO ti3; +affected rows: 0 +ALTER TABLE tm1 RENAME TO tm3; +affected rows: 0 +ALTER TABLE ti3 RENAME TO ti1; +affected rows: 0 +ALTER TABLE tm3 RENAME TO tm1; +affected rows: 0 +ALTER TABLE ti1 ORDER BY b; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 ORDER BY b; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 CONVERT TO CHARACTER SET utf16; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 CONVERT TO CHARACTER SET utf16; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 DEFAULT CHARACTER SET utf8; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 DEFAULT CHARACTER SET utf8; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 FORCE; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 FORCE; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 AUTO_INCREMENT 3; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 AUTO_INCREMENT 3; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 AVG_ROW_LENGTH 10; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 AVG_ROW_LENGTH 10; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 CHECKSUM 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 CHECKSUM 1; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 COMMENT 'test'; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 COMMENT 'test'; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MAX_ROWS 100; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MAX_ROWS 100; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 MIN_ROWS 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 MIN_ROWS 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti1 PACK_KEYS 1; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE tm1 PACK_KEYS 1; +affected rows: 2 +info: Records: 2 Duplicates: 0 Warnings: 0 +DROP TABLE ti1, ti2, tm1, tm2; +# Tests of >1 operation (InnoDB) +CREATE TABLE ti1(a INT PRIMARY KEY AUTO_INCREMENT, b INT) engine=InnoDB; +INSERT INTO ti1(b) VALUES (1), (2); +ALTER TABLE ti1 RENAME TO ti3, ADD INDEX ii1(b); +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +ALTER TABLE ti3 DROP INDEX ii1, AUTO_INCREMENT 5; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +INSERT INTO ti3(b) VALUES (5); +ALTER TABLE ti3 ADD INDEX ii1(b), AUTO_INCREMENT 7; +affected rows: 0 +info: Records: 0 Duplicates: 0 Warnings: 0 +INSERT INTO ti3(b) VALUES (7); +SELECT * FROM ti3; +a b +1 1 +2 2 +5 5 +7 7 +DROP TABLE ti3; +# +# 8: Scenario in which ALTER TABLE was returning an unwarranted +# ER_ILLEGAL_HA error at some point during work on this WL. +# +CREATE TABLE tm1(i INT DEFAULT 1) engine=MyISAM; +ALTER TABLE tm1 ADD INDEX ii1(i), ALTER COLUMN i DROP DEFAULT; +DROP TABLE tm1; diff --git a/mysql-test/r/alter_table_online.result b/mysql-test/r/alter_table_online.result index 83e82191541..1e7bc5e83cd 100644 --- a/mysql-test/r/alter_table_online.result +++ b/mysql-test/r/alter_table_online.result @@ -11,61 +11,59 @@ drop table t1; create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b')); insert into t1 (a) values (1),(2),(3); alter online table t1 modify b int default 5; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 change b new_name int; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify e enum('a','b','c'); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 comment "new comment"; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 rename to t2; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. drop table t1; create table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b')); insert into t1 (a) values (1),(2),(3); alter online table t1 drop column b, add b int; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify b bigint; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify e enum('c','a','b'); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify c varchar(50); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify c varchar(100); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 add f int; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 engine=memory; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter table t1 engine=innodb; alter table t1 add index (b); alter online table t1 add index c (c); -ERROR HY000: Can't execute the given 'ALTER' command as online alter online table t1 drop index b; -ERROR HY000: Can't execute the given 'ALTER' command as online drop table t1; create temporary table t1 (a int not null primary key, b int, c varchar(80), e enum('a','b')); insert into t1 (a) values (1),(2),(3); alter online table t1 drop column b, add b int; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify b bigint; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify e enum('c','a','b'); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify c varchar(50); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 modify c varchar(100); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 add f int; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 engine=memory; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter table t1 engine=innodb; alter table t1 add index (b); alter online table t1 add index c (c); -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. alter online table t1 drop index b; -ERROR HY000: Can't execute the given 'ALTER' command as online +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. drop table t1; create table t1 (a int not null primary key, b int, c varchar(80)); create table t2 (a int not null primary key, b int, c varchar(80)); diff --git a/mysql-test/r/bootstrap.result b/mysql-test/r/bootstrap.result index 8bef6f90ab4..2e2082441f8 100644 --- a/mysql-test/r/bootstrap.result +++ b/mysql-test/r/bootstrap.result @@ -1,7 +1,7 @@ drop table if exists t1; drop table t1; drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' set @my_max_allowed_packet= @@max_allowed_packet; set global max_allowed_packet=100*@@max_allowed_packet; set global max_allowed_packet=@my_max_allowed_packet; diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index 03b4b84e461..3b57b4833a9 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -768,13 +768,19 @@ CAST(CAST('20:05:05' AS TIME) as date) set sql_mode= TRADITIONAL; select cast("2101-00-01 02:03:04" as datetime); cast("2101-00-01 02:03:04" as datetime) -2101-00-01 02:03:04 +NULL +Warnings: +Warning 1292 Incorrect datetime value: '2101-00-01 02:03:04' select cast(cast("2101-00-01 02:03:04" as datetime) as time); cast(cast("2101-00-01 02:03:04" as datetime) as time) -02:03:04 +NULL +Warnings: +Warning 1292 Incorrect datetime value: '2101-00-01 02:03:04' SELECT CAST(CAST('20:05:05' AS TIME) as date); CAST(CAST('20:05:05' AS TIME) as date) -0000-00-00 +NULL +Warnings: +Warning 1292 Truncated incorrect date value: '0000-00-00' set sql_mode=DEFAULT; create table t1 (f1 time, f2 date, f3 datetime); insert into t1 values ('11:22:33','2011-12-13','2011-12-13 11:22:33'); diff --git a/mysql-test/r/commit_1innodb.result b/mysql-test/r/commit_1innodb.result index af198edc4ca..3583e8ed396 100644 --- a/mysql-test/r/commit_1innodb.result +++ b/mysql-test/r/commit_1innodb.result @@ -1,6 +1,6 @@ call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); set sql_mode=no_engine_substitution; -set storage_engine = InnoDB; +set default_storage_engine = InnoDB; set autocommit=1; drop table if exists t1; drop table if exists t2; @@ -842,7 +842,7 @@ call p_verify_status_increment(2, 0, 2, 0); SUCCESS alter table t3 rename t4; -call p_verify_status_increment(2, 0, 2, 0); +call p_verify_status_increment(0, 0, 0, 0); SUCCESS rename table t4 to t3; diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 3fcc5b5d2c1..84a1e9dbab2 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -16,15 +16,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -59,15 +55,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -110,15 +102,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index d0d953f4e38..fff8733cfdf 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -28,15 +28,15 @@ create table t2 select auto+1 from t1; ERROR 42S02: Table 'test.t1' doesn't exist drop table if exists t1,t2; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' create table t1 (b char(0) not null, index(b)); ERROR 42000: The storage engine MyISAM can't index column `b` create table t1 (a int not null,b text) engine=heap; ERROR 42000: Storage engine MEMORY doesn't support BLOB/TEXT columns drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' create table t1 (ordid int(8) not null auto_increment, ord varchar(50) not null, primary key (ord,ordid)) engine=heap; ERROR 42000: Incorrect table definition; there can be only one auto column and it must be defined as a key create table not_existing_database.test (a int); @@ -157,17 +157,17 @@ create table t2 (a int, a float) select * from t1; ERROR 42S21: Duplicate column name 'a' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' create table t2 (a int) select a as b, a+1 as b from t1; ERROR 42S21: Duplicate column name 'b' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' create table t2 (b int) select a as b, a+1 as b from t1; ERROR 42S21: Duplicate column name 'b' drop table if exists t1,t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t1 (a int not null); INSERT INTO t1 values (1),(2),(1); CREATE TABLE t2 (primary key(a)) SELECT * FROM t1; @@ -177,7 +177,7 @@ ERROR 42S02: Table 'test.t2' doesn't exist DROP TABLE t1; DROP TABLE IF EXISTS t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' create table t1 (a int not null, b int, primary key(a), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b), key (b)); show create table t1; Table Create Table @@ -1610,12 +1610,12 @@ CREATE TABLE t2 (primary key (a)) select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TEMPORARY TABLE t2 (primary key (a)) select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t2 (a int, b int, primary key (a)); INSERT INTO t2 select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' diff --git a/mysql-test/r/ctype_errors.result b/mysql-test/r/ctype_errors.result index 90d0c28eebf..5ae8c53ce8b 100644 --- a/mysql-test/r/ctype_errors.result +++ b/mysql-test/r/ctype_errors.result @@ -24,11 +24,11 @@ lc_messages ru_RU SET GLOBAL lc_messages=en_US; DROP TABLE t1; drop table `×§`; -ERROR 42S02: Unknown table '×§' +ERROR 42S02: Unknown table 'test.×§' SET lc_messages=cs_CZ; SET NAMES UTF8; USE nonexistant; -ERROR 42000: Nezn-Bámá databáze 'nonexistant' +ERROR 42000: Neznámá databáze 'nonexistant' # # Bug#12736295: Buffer overflow for variable converted_err # with non-latin1 server error message diff --git a/mysql-test/r/ctype_tis620.result b/mysql-test/r/ctype_tis620.result index 5699c044d70..c86b8392b32 100644 --- a/mysql-test/r/ctype_tis620.result +++ b/mysql-test/r/ctype_tis620.result @@ -138,7 +138,7 @@ year DROP TABLE t1; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 ( name varchar(50) NOT NULL default '', diff --git a/mysql-test/r/ctype_ujis.result b/mysql-test/r/ctype_ujis.result index b801a7f45a4..3db6aee37cc 100644 --- a/mysql-test/r/ctype_ujis.result +++ b/mysql-test/r/ctype_ujis.result @@ -94,7 +94,7 @@ select @ujis4 = CONVERT(@utf84 USING ujis); 1 drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' create table t1 (c1 varchar(8)) default character set 'ujis'; insert into t1 values (0xA4A2),(0xA2A2),(0xA4A2); select c1 as 'no index' from t1 where c1 like cast(concat(0xA4A2, '%') as char character set ujis); @@ -168,7 +168,7 @@ a b DROP TABLE t1; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(c char(1)) character set ujis; INSERT INTO t1 VALUES(0xA2AF); INSERT INTO t1 VALUES(0xA2B0); diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index d25c454913d..714b4183594 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -1229,7 +1229,7 @@ DROP TABLE t1; SET NAMES utf8; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=MyISAM DEFAULT CHARSET=utf8; INSERT INTO t1 VALUES('uuABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t1 VALUES('uu'); diff --git a/mysql-test/r/ctype_utf8mb4.result b/mysql-test/r/ctype_utf8mb4.result index d8642955b89..f4be208e0f7 100644 --- a/mysql-test/r/ctype_utf8mb4.result +++ b/mysql-test/r/ctype_utf8mb4.result @@ -1256,7 +1256,7 @@ DROP TABLE t1; SET NAMES utf8mb4; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; Warnings: Warning 1071 Specified key was too long; max key length is 1000 bytes @@ -2452,7 +2452,6 @@ MODIFY subject varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY p varchar(255) CHARACTER SET utf8; Warnings: Warning 1071 Specified key was too long; max key length is 1000 bytes -Warning 1071 Specified key was too long; max key length is 1000 bytes SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -2539,8 +2538,7 @@ t2 CREATE TABLE `t2` ( ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1, t2; # -# Bug#13581962 HIGH MEMORY USAGE ATTEMPT, THEN CRASH WITH -# LONGTEXT, UNION, USER VARIABLE +# Bug#13581962 HIGH MEMORY USAGE ATTEMPT, THEN CRASH WITH LONGTEXT, UNION, USER VARIABLE # Bug#14096619 UNABLE TO RESTORE DATABASE DUMP # CREATE TABLE t1(f1 LONGTEXT CHARACTER SET utf8mb4); diff --git a/mysql-test/r/ctype_utf8mb4_heap.result b/mysql-test/r/ctype_utf8mb4_heap.result index 63de75b37b7..94ea59c1a0c 100644 --- a/mysql-test/r/ctype_utf8mb4_heap.result +++ b/mysql-test/r/ctype_utf8mb4_heap.result @@ -1160,7 +1160,7 @@ DROP TABLE t1; SET NAMES utf8mb4; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=heap DEFAULT CHARSET=utf8mb4; INSERT INTO t1 VALUES('uuABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); INSERT INTO t1 VALUES('uu'); diff --git a/mysql-test/r/ctype_utf8mb4_innodb.result b/mysql-test/r/ctype_utf8mb4_innodb.result index 2db7066d478..b0e5bcef176 100644 --- a/mysql-test/r/ctype_utf8mb4_innodb.result +++ b/mysql-test/r/ctype_utf8mb4_innodb.result @@ -1231,7 +1231,7 @@ DROP TABLE t1; SET NAMES utf8mb4; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; Warnings: Warning 1071 Specified key was too long; max key length is 767 bytes diff --git a/mysql-test/r/ctype_utf8mb4_myisam.result b/mysql-test/r/ctype_utf8mb4_myisam.result index b82e5687eda..6f5d79ff6df 100644 --- a/mysql-test/r/ctype_utf8mb4_myisam.result +++ b/mysql-test/r/ctype_utf8mb4_myisam.result @@ -1231,7 +1231,7 @@ DROP TABLE t1; SET NAMES utf8mb4; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(a VARCHAR(255), KEY(a)) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; Warnings: Warning 1071 Specified key was too long; max key length is 1000 bytes diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result index 55309e54fb3..209eb896978 100644 --- a/mysql-test/r/drop.result +++ b/mysql-test/r/drop.result @@ -2,7 +2,7 @@ drop table if exists t1; drop database if exists mysqltest; drop database if exists client_test_db; drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' create table t1(n int); insert into t1 values(1); create temporary table t1( n int); @@ -30,13 +30,13 @@ table7, table8, table9, table10, table11, table12, table13, table14, table15, table16, table17, table18, table19, table20, table21, table22, table23, table24, table25, table26, table27, table28; -ERROR 42S02: Unknown table 'table1,table2,table3,table4,table5,table6,table7,table8,table9,table10,table11,table12,table13,table' +ERROR 42S02: Unknown table 'mysqltest.table1,mysqltest.table2,mysqltest.table3,mysqltest.table4,mysqltest.table5,mysqltest.table' drop table table1, table2, table3, table4, table5, table6, table7, table8, table9, table10, table11, table12, table13, table14, table15, table16, table17, table18, table19, table20, table21, table22, table23, table24, table25, table26, table27, table28, table29, table30; -ERROR 42S02: Unknown table 'table1,table2,table3,table4,table5,table6,table7,table8,table9,table10,table11,table12,table13,table' +ERROR 42S02: Unknown table 'mysqltest.table1,mysqltest.table2,mysqltest.table3,mysqltest.table4,mysqltest.table5,mysqltest.table' use test; drop database mysqltest; flush tables with read lock; @@ -154,10 +154,10 @@ End of 5.1 tests # -- DROP TABLE IF EXISTS t1; DROP TABLE t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' SHOW WARNINGS; Level Code Message -Error 1051 Unknown table 't1' +Error 1051 Unknown table 'test.t1' # -- # -- End of Bug#37431. diff --git a/mysql-test/r/dyncol.result b/mysql-test/r/dyncol.result index 172179bd7f4..c5040728af7 100644 --- a/mysql-test/r/dyncol.result +++ b/mysql-test/r/dyncol.result @@ -1036,9 +1036,7 @@ Warnings: Warning 1292 Truncated incorrect time value: '2011-13-01 8:46:06.23434' select column_get(column_create(1, "2011-02-30 8:46:06.23434" AS CHAR), 1 as time); column_get(column_create(1, "2011-02-30 8:46:06.23434" AS CHAR), 1 as time) -NULL -Warnings: -Warning 1292 Truncated incorrect time value: '2011-02-30 8:46:06.23434' +08:46:06 select column_get(column_create(1, "2001-02-03"), 1 as time); column_get(column_create(1, "2001-02-03"), 1 as time) 00:20:01 diff --git a/mysql-test/r/error_simulation.result b/mysql-test/r/error_simulation.result index d2f5a24ef1d..88a9d114bc6 100644 --- a/mysql-test/r/error_simulation.result +++ b/mysql-test/r/error_simulation.result @@ -1,6 +1,6 @@ DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 ( a varchar(32) character set utf8 collate utf8_bin NOT NULL, b varchar(32) character set utf8 collate utf8_bin NOT NULL ) diff --git a/mysql-test/r/events_restart.result b/mysql-test/r/events_restart.result index 6a751fa29f8..ba3aa503b63 100644 --- a/mysql-test/r/events_restart.result +++ b/mysql-test/r/events_restart.result @@ -65,3 +65,26 @@ select @@event_scheduler; ON drop table execution_log; drop database events_test; +# +# Test for bug#11748899 -- EVENT SET TO DISABLED AND ON COMPLETION +# NOT PRESERVE IS DELETED AT SERVER +# +SELECT @@event_scheduler; +@@event_scheduler +ON +USE test; +DROP EVENT IF EXISTS e1; +CREATE EVENT e1 ON SCHEDULE EVERY 1 SECOND DISABLE DO SELECT 1; +SHOW EVENTS; +Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation +test e1 root@localhost SYSTEM RECURRING # 1 SECOND # # DISABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci +"Now we restart the server" +USE test; +SELECT @@event_scheduler; +@@event_scheduler +ON +SHOW EVENTS; +Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation +test e1 root@localhost SYSTEM RECURRING # 1 SECOND # # DISABLED 1 latin1 latin1_swedish_ci latin1_swedish_ci +DROP EVENT e1; +# end test for bug#11748899 diff --git a/mysql-test/r/flush_read_lock.result b/mysql-test/r/flush_read_lock.result index 05fab64330d..c2e8531d01f 100644 --- a/mysql-test/r/flush_read_lock.result +++ b/mysql-test/r/flush_read_lock.result @@ -544,11 +544,10 @@ Success: Was not able to run 'drop table t2_base' under FTWRL. Success: 'drop table t2_base' is blocked by FTWRL active in another connection. Success: FTWRL is blocked when 'drop table t2_base' is active in another connection. # 13.1.b) DROP TABLES which affects only temporary tables -# in theory can be compatible with FTWRL. -# In practice it is not yet. -Success: Was not able to run 'drop table t2_temp' under FTWRL. -Success: 'drop table t2_temp' is blocked by FTWRL active in another connection. -Success: FTWRL is blocked when 'drop table t2_temp' is active in another connection. +# is compatible with FTWRL. +Success: Was able to run 'drop table t2_temp' under FTWRL. +Success: Was able to run 'drop table t2_temp' with FTWRL active in another connection. +Success: Was able to run FTWRL while 'drop table t2_temp' was active in another connection. # # 13.1.c) DROP TEMPORARY TABLES should be compatible with FTWRL. Success: Was able to run 'drop temporary table t2_temp' under FTWRL. @@ -1461,24 +1460,10 @@ Success: Was able to run 'analyze table t3_temp_trans' under FTWRL. Success: Was able to run 'analyze table t3_temp_trans' with FTWRL active in another connection. Success: Was able to run FTWRL while 'analyze table t3_temp_trans' was active in another connection. # -# 39.2.c) Some statements do implicit commit and not -# considered read-only. As result they are -# not compatible with FTWRL. -# -flush tables with read lock; -# Implicit commits are allowed under FTWRL. -alter table t3_temp_trans add column c1 int; -unlock tables; -# -# Switching to connection 'con1'. -flush tables with read lock; -# Switching to connection 'default'. -alter table t3_temp_trans drop column c1; -# Switching to connection 'con1'. -# Check that ALTER TABLE is blocked. -unlock tables; -# Switching to connection 'default'. -# Reap ALTER TABLE +# And ALTER TABLE: +Success: Was able to run 'alter table t3_temp_trans add column c1 int' under FTWRL. +Success: Was able to run 'alter table t3_temp_trans add column c1 int' with FTWRL active in another connection. +Success: Was able to run FTWRL while 'alter table t3_temp_trans add column c1 int' was active in another connection. # # 40) Test effect of implicit commit for DDL which is otherwise # compatible with FTWRL. Implicit commit at the start of DDL diff --git a/mysql-test/r/func_analyse.result b/mysql-test/r/func_analyse.result index f82439090f6..2c300559a32 100644 --- a/mysql-test/r/func_analyse.result +++ b/mysql-test/r/func_analyse.result @@ -128,7 +128,7 @@ End of 5.0 tests # DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 (a VARCHAR(2) CHARSET UTF8 NOT NULL); INSERT INTO t1 VALUES ('e'),('e'),('e-'); SELECT * FROM t1 PROCEDURE ANALYSE(); diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index c2f369b3941..1eda56ac114 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -43,7 +43,7 @@ old_password(NULL) NULL select password(NULL); password(NULL) -NULL + set global old_passwords=on; select password(''); password('') diff --git a/mysql-test/r/func_rollback.result b/mysql-test/r/func_rollback.result index 57968910051..91151302a06 100644 --- a/mysql-test/r/func_rollback.result +++ b/mysql-test/r/func_rollback.result @@ -190,8 +190,6 @@ END; SELECT f1_insert_select(2); f1_insert_select(2) 1 -Warnings: -Warning 1048 Column 'f2' cannot be null SELECT * FROM t1_not_null ORDER BY f1,f2; f1 f2 2 0 @@ -267,8 +265,6 @@ END; SELECT f1_insert_with_two_rows(); f1_insert_with_two_rows() 1 -Warnings: -Warning 1048 Column 'f2' cannot be null SELECT * FROM t1_not_null ORDER BY f1,f2; f1 f2 10 0 diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index 72c7a5a128f..ace7283e192 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -62,7 +62,9 @@ datediff("1997-11-30 23:59:59.000001","1997-12-31") SET @@SQL_MODE="ALLOW_INVALID_DATES"; select datediff("1997-11-31 23:59:59.000001","1997-12-31"); datediff("1997-11-31 23:59:59.000001","1997-12-31") --30 +NULL +Warnings: +Warning 1292 Incorrect datetime value: '1997-11-31 23:59:59.000001' SET @@SQL_MODE=""; select datediff("1997-11-31 23:59:59.000001","1997-12-31"); datediff("1997-11-31 23:59:59.000001","1997-12-31") diff --git a/mysql-test/r/gis-rtree.result b/mysql-test/r/gis-rtree.result index 22c30479125..c400ebb39ef 100644 --- a/mysql-test/r/gis-rtree.result +++ b/mysql-test/r/gis-rtree.result @@ -712,7 +712,7 @@ count(*) DROP TABLE t2; drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 (a geometry NOT NULL, SPATIAL (a)); INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); INSERT INTO t1 VALUES (GeomFromText("LINESTRING(100 100, 200 200, 300 300)")); diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index eb9f1a57c32..fa7b93092d9 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -918,25 +918,25 @@ SELECT Overlaps(@horiz1, @point2) FROM DUAL; Overlaps(@horiz1, @point2) 0 DROP TABLE t1; -create table t1(f1 geometry, f2 point, f3 linestring); +create table t1(f1 geometry, f2 linestring, f3 linestring); select f1 from t1 union select f1 from t1; f1 -insert into t1 (f2,f3) values (GeomFromText('POINT(1 1)'), +insert into t1 (f2,f3) values (GeomFromText('LINESTRING(1 1, 2 2)'), GeomFromText('LINESTRING(0 0,1 1,2 2)')); select AsText(f2),AsText(f3) from t1; AsText(f2) AsText(f3) -POINT(1 1) LINESTRING(0 0,1 1,2 2) +LINESTRING(1 1,2 2) LINESTRING(0 0,1 1,2 2) select AsText(a) from (select f2 as a from t1 union select f3 from t1) t; AsText(a) -POINT(1 1) +LINESTRING(1 1,2 2) LINESTRING(0 0,1 1,2 2) create table t2 as select f2 as a from t1 union select f3 from t1; desc t2; Field Type Null Key Default Extra -a point YES NULL +a linestring YES NULL select AsText(a) from t2; AsText(a) -POINT(1 1) +LINESTRING(1 1,2 2) LINESTRING(0 0,1 1,2 2) drop table t1, t2; SELECT 1; diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index f1cf94e4f19..be05f17c281 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -55,6 +55,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -126,6 +127,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 @@ -173,6 +175,7 @@ max_connections 30 max_user_connections 0 plugin authentication_string +password_expired N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 @@ -1353,7 +1356,7 @@ FLUSH PRIVILEGES; DROP TABLE mysql.user; drop table if exists test; Warnings: -Note 1051 Unknown table 'test' +Note 1051 Unknown table 'test.test' drop function if exists test_function; Warnings: Note 1305 FUNCTION test.test_function does not exist diff --git a/mysql-test/r/handlersocket.result b/mysql-test/r/handlersocket.result index 765d954d3dc..e1fbc2d9840 100644 --- a/mysql-test/r/handlersocket.result +++ b/mysql-test/r/handlersocket.result @@ -5,7 +5,7 @@ plugin_version 1.0 plugin_status ACTIVE plugin_type DAEMON plugin_library handlersocket.so -plugin_library_version 1.5 +plugin_library_version 1.7 plugin_author higuchi dot akira at dena dot jp plugin_description Direct access into InnoDB plugin_license BSD diff --git a/mysql-test/r/innodb_ext_key.result b/mysql-test/r/innodb_ext_key.result index 4a6b902e869..e4e7e44ce7c 100644 --- a/mysql-test/r/innodb_ext_key.result +++ b/mysql-test/r/innodb_ext_key.result @@ -842,6 +842,10 @@ engine=innodb; insert into t3 select a,a,a,a from t2; alter table t3 add primary key (pk1, pk2); alter table t3 add key (col1, col2); +analyze table t1,t3; +Table Op Msg_type Msg_text +test.t1 analyze status OK +test.t3 analyze status OK set optimizer_switch='extended_keys=off'; explain select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; @@ -852,7 +856,7 @@ explain select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a and t3.pk1=t1.a; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL # Using where -1 SIMPLE t3 ref PRIMARY,col1 PRIMARY 4 test.t1.a # Using where +1 SIMPLE t3 ref PRIMARY,col1 col1 8 test.t1.a,test.t1.a # Using where; Using index set optimizer_switch='extended_keys=on'; explain select * from t1, t3 where t3.col1=t1.a and t3.col2=t1.a; diff --git a/mysql-test/r/innodb_mysql_sync.result b/mysql-test/r/innodb_mysql_sync.result index 7c41ffec344..2164b936938 100644 --- a/mysql-test/r/innodb_mysql_sync.result +++ b/mysql-test/r/innodb_mysql_sync.result @@ -101,7 +101,7 @@ DROP TABLE IF EXISTS t1; CREATE DATABASE db1; CREATE TABLE db1.t1(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, value INT) engine=innodb; INSERT INTO db1.t1(value) VALUES (1), (2); -SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query"; +SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; # Sending: ALTER TABLE db1.t1 ADD INDEX(value); # Connection con1 @@ -115,45 +115,47 @@ SET DEBUG_SYNC= "now SIGNAL query"; # Connection default # Reaping: ALTER TABLE db1.t1 ADD INDEX(value) DROP DATABASE db1; -# Test 2: Primary index (implicit), should block reads. +# Test 2: Primary index (implicit), should block writes. CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb; -SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query"; +SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; # Sending: -ALTER TABLE t1 ADD UNIQUE INDEX(a); +ALTER TABLE t1 ADD UNIQUE INDEX(a), LOCK=SHARED; # Connection con1 SET DEBUG_SYNC= "now WAIT_FOR manage"; USE test; -# Sending: SELECT * FROM t1; +a b +# Sending: +UPDATE t1 SET a=NULL; # Connection con2 # Waiting for SELECT to be blocked by the metadata lock on t1 SET DEBUG_SYNC= "now SIGNAL query"; # Connection default # Reaping: ALTER TABLE t1 ADD UNIQUE INDEX(a) # Connection con1 -# Reaping: SELECT * FROM t1 -a b -# Test 3: Primary index (explicit), should block reads. +# Reaping: UPDATE t1 SET a=NULL +# Test 3: Primary index (explicit), should block writes. # Connection default ALTER TABLE t1 DROP INDEX a; -SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query"; +SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; # Sending: -ALTER TABLE t1 ADD PRIMARY KEY (a); +ALTER TABLE t1 ADD PRIMARY KEY (a), LOCK=SHARED; # Connection con1 SET DEBUG_SYNC= "now WAIT_FOR manage"; -# Sending: SELECT * FROM t1; +a b +# Sending: +UPDATE t1 SET a=NULL; # Connection con2 # Waiting for SELECT to be blocked by the metadata lock on t1 SET DEBUG_SYNC= "now SIGNAL query"; # Connection default # Reaping: ALTER TABLE t1 ADD PRIMARY KEY (a) # Connection con1 -# Reaping: SELECT * FROM t1 -a b +# Reaping: UPDATE t1 SET a=NULL # Test 4: Secondary unique index, should not block reads. # Connection default -SET DEBUG_SYNC= "alter_table_manage_keys SIGNAL manage WAIT_FOR query"; +SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; # Sending: ALTER TABLE t1 ADD UNIQUE (b); # Connection con1 @@ -186,3 +188,170 @@ a b 1 12345 2 23456 DROP TABLE t1; +# +# Bug#13417754 ASSERT IN ROW_DROP_DATABASE_FOR_MYSQL DURING DROP SCHEMA +# +DROP TABLE IF EXISTS t1; +DROP DATABASE IF EXISTS db1; +CREATE TABLE t1(a int) engine=InnoDB; +CREATE DATABASE db1; +# Connection con1 +SET DEBUG_SYNC= 'after_innobase_rename_table SIGNAL locked WAIT_FOR continue'; +# Sending: +ALTER TABLE t1 RENAME db1.t1; +# Connection con2 +SET DEBUG_SYNC= 'now WAIT_FOR locked'; +# DROP DATABASE db1 should now be blocked by ALTER TABLE +# Sending: +DROP DATABASE db1; +# Connection default +# Check that DROP DATABASE is blocked by IX lock on db1 +# Resume ALTER TABLE +SET DEBUG_SYNC= 'now SIGNAL continue'; +# Connection con1 +# Reaping: ALTER TABLE t1 RENAME db1.t1; +# Connection con2 +# Reaping: DROP DATABASE db1 +# Connection default; +SET DEBUG_SYNC= 'RESET'; +# +# WL#5534 Online ALTER, Phase 1 +# +# Multi thread tests. +# See alter_table.test for single thread tests. +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(a INT PRIMARY KEY, b INT) engine=InnoDB; +INSERT INTO t1 VALUES (1,1), (2,2); +SET DEBUG_SYNC= 'RESET'; +SET SESSION lock_wait_timeout= 1; +# +# 1: In-place + writes blocked. +# +# Connection default +SET DEBUG_SYNC= 'alter_opened_table SIGNAL opened WAIT_FOR continue1'; +SET DEBUG_SYNC= 'alter_table_inplace_after_lock_upgrade SIGNAL upgraded WAIT_FOR continue2'; +SET DEBUG_SYNC= 'alter_table_inplace_before_commit SIGNAL beforecommit WAIT_FOR continue3'; +SET DEBUG_SYNC= 'alter_table_before_main_binlog SIGNAL binlog WAIT_FOR continue4'; +# Sending: +ALTER TABLE t1 ADD INDEX i1(b), ALGORITHM= INPLACE, LOCK= SHARED; +# Connection con1; +SET DEBUG_SYNC= 'now WAIT_FOR opened'; +# At this point, neither reads nor writes should be blocked. +SELECT * FROM t1; +a b +1 1 +2 2 +INSERT INTO t1 VALUES (3,3); +SET DEBUG_SYNC= 'now SIGNAL continue1'; +SET DEBUG_SYNC= 'now WAIT_FOR upgraded'; +# Now both reads and writes should be blocked +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (4,4); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue2'; +SET DEBUG_SYNC= 'now WAIT_FOR beforecommit'; +# Still both reads and writes should be blocked. +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (5,5); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue3'; +SET DEBUG_SYNC= 'now WAIT_FOR binlog'; +# Same here. +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (6,6); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue4'; +# Connection default +# Reaping ALTER TABLE ... +SET DEBUG_SYNC= 'RESET'; +DELETE FROM t1 WHERE a= 3; +# +# 2: Copy + writes blocked. +# +SET DEBUG_SYNC= 'alter_opened_table SIGNAL opened WAIT_FOR continue1'; +SET DEBUG_SYNC= 'alter_table_copy_after_lock_upgrade SIGNAL upgraded WAIT_FOR continue2'; +SET DEBUG_SYNC= 'alter_table_before_main_binlog SIGNAL binlog WAIT_FOR continue3'; +# Sending: +ALTER TABLE t1 ADD INDEX i2(b), ALGORITHM= COPY, LOCK= SHARED; +# Connection con1; +SET DEBUG_SYNC= 'now WAIT_FOR opened'; +# At this point, neither reads nor writes should be blocked. +SELECT * FROM t1; +a b +1 1 +2 2 +INSERT INTO t1 VALUES (3,3); +SET DEBUG_SYNC= 'now SIGNAL continue1'; +SET DEBUG_SYNC= 'now WAIT_FOR upgraded'; +# Now writes should be blocked, reads still allowed. +SELECT * FROM t1; +a b +1 1 +2 2 +3 3 +INSERT INTO t1 VALUES (4,4); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue2'; +SET DEBUG_SYNC= 'now WAIT_FOR binlog'; +# Now both reads and writes should be blocked. +SELECT * FROM t1 limit 1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (5,5); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue3'; +# Connection default +# Reaping ALTER TABLE ... +SET DEBUG_SYNC= 'RESET'; +DELETE FROM t1 WHERE a= 3; +# +# 3: In-place + writes allowed. +# +# TODO: Enable this test once WL#5526 is pushed +# +# 4: In-place + reads and writes blocked. +# +# Connection default +SET DEBUG_SYNC= 'alter_opened_table SIGNAL opened WAIT_FOR continue1'; +SET DEBUG_SYNC= 'alter_table_inplace_after_lock_upgrade SIGNAL upgraded WAIT_FOR continue2'; +SET DEBUG_SYNC= 'alter_table_inplace_before_commit SIGNAL beforecommit WAIT_FOR continue3'; +SET DEBUG_SYNC= 'alter_table_before_main_binlog SIGNAL binlog WAIT_FOR continue4'; +# Sending: +ALTER TABLE t1 ADD INDEX i4(b), ALGORITHM= INPLACE, LOCK= EXCLUSIVE; +# Connection con1; +SET DEBUG_SYNC= 'now WAIT_FOR opened'; +# At this point, neither reads nor writes should be blocked. +SELECT * FROM t1; +a b +1 1 +2 2 +INSERT INTO t1 VALUES (3,3); +SET DEBUG_SYNC= 'now SIGNAL continue1'; +SET DEBUG_SYNC= 'now WAIT_FOR upgraded'; +# Now both reads and writes should be blocked. +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (4,4); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue2'; +SET DEBUG_SYNC= 'now WAIT_FOR beforecommit'; +# Same here. +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (5,5); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue3'; +SET DEBUG_SYNC= 'now WAIT_FOR binlog'; +# Same here. +SELECT * FROM t1; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +INSERT INTO t1 VALUES (6,6); +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +SET DEBUG_SYNC= 'now SIGNAL continue4'; +# Connection default +# Reaping ALTER TABLE ... +SET DEBUG_SYNC= 'RESET'; +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; diff --git a/mysql-test/r/join_outer_innodb.result b/mysql-test/r/join_outer_innodb.result index 1081fc0eed3..95f6b3ab9a2 100644 --- a/mysql-test/r/join_outer_innodb.result +++ b/mysql-test/r/join_outer_innodb.result @@ -481,9 +481,9 @@ drop table t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16; # drop table if exists t1,t2,t3; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' +Note 1051 Unknown table 'test.t3' create table t2(a int,unique key (a)) engine=innodb; create table t3(b int) engine=innodb; create table t1(a int,b int)engine=innodb; diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result index e63afeab126..cb21e5e8134 100644 --- a/mysql-test/r/key.result +++ b/mysql-test/r/key.result @@ -273,7 +273,7 @@ t drop table t1; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 ( c1 int, c2 varbinary(240), diff --git a/mysql-test/r/log_slow.result b/mysql-test/r/log_slow.result index 6500ba3ca53..4414a32d821 100644 --- a/mysql-test/r/log_slow.result +++ b/mysql-test/r/log_slow.result @@ -56,6 +56,7 @@ last_insert_id int(11) NO NULL insert_id int(11) NO NULL server_id int(10) unsigned NO NULL sql_text mediumtext NO NULL +thread_id bigint(21) unsigned NO NULL flush slow logs; set long_query_time=0.1; set log_slow_filter=''; diff --git a/mysql-test/r/log_state.result b/mysql-test/r/log_state.result index 3ccd1451bc4..1ce7eb0d2aa 100644 --- a/mysql-test/r/log_state.result +++ b/mysql-test/r/log_state.result @@ -45,7 +45,7 @@ select sleep(@long_query_time + 1); sleep(@long_query_time + 1) 0 select * from mysql.slow_log where sql_text NOT LIKE '%slow_log%'; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id # Switch to connection default set global slow_query_log= ON; # Switch to connection con1 @@ -54,8 +54,8 @@ select sleep(@long_query_time + 1); sleep(@long_query_time + 1) 0 select * from mysql.slow_log where sql_text NOT LIKE '%slow_log%'; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text -TIMESTAMP USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 select sleep(@long_query_time + 1) +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id +TIMESTAMP USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 select sleep(@long_query_time + 1) THREAD_ID # Switch to connection default show global variables where Variable_name = 'log' or Variable_name = 'log_slow_queries' or diff --git a/mysql-test/r/log_tables.result b/mysql-test/r/log_tables.result index 18da8765d4b..4471c01c99b 100644 --- a/mysql-test/r/log_tables.result +++ b/mysql-test/r/log_tables.result @@ -17,7 +17,7 @@ event_time user_host thread_id server_id command_type argument TIMESTAMP USER_HOST THREAD_ID 1 Query select * from general_log truncate table slow_log; select * from slow_log; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id truncate table general_log; select * from general_log where argument like '%general_log%'; event_time user_host thread_id server_id command_type argument @@ -55,7 +55,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -64,7 +64,7 @@ show fields from mysql.general_log; Field Type Null Key Default Extra event_time timestamp(6) NO CURRENT_TIMESTAMP(6) on update CURRENT_TIMESTAMP user_host mediumtext NO NULL -thread_id int(11) NO NULL +thread_id bigint(21) unsigned NO NULL server_id int(10) unsigned NO NULL command_type varchar(64) NO NULL argument mediumtext NO NULL @@ -81,7 +81,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' show fields from mysql.slow_log; Field Type Null Key Default Extra @@ -96,6 +97,7 @@ last_insert_id int(11) NO NULL insert_id int(11) NO NULL server_id int(10) unsigned NO NULL sql_text mediumtext NO NULL +thread_id bigint(21) unsigned NO NULL flush logs; flush tables; SET GLOBAL GENERAL_LOG=ON; @@ -146,8 +148,8 @@ select sleep(2); sleep(2) 0 select * from mysql.slow_log; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text -TIMESTAMP USER_HOST QUERY_TIME 00:00:00.000000 1 0 mysql 0 0 1 select sleep(2) +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id +TIMESTAMP USER_HOST QUERY_TIME 00:00:00.000000 1 0 mysql 0 0 1 select sleep(2) THREAD_ID set @@session.long_query_time = @saved_long_query_time; alter table mysql.general_log engine=myisam; ERROR HY000: You cannot 'ALTER' a log table if logging is enabled @@ -166,7 +168,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -184,7 +186,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' alter table mysql.general_log engine=myisam; alter table mysql.slow_log engine=myisam; @@ -193,7 +196,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -211,7 +214,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Slow log' set global general_log='ON'; set global slow_query_log='ON'; @@ -256,15 +260,15 @@ set storage_engine= @save_storage_engine; drop table mysql.slow_log; drop table mysql.general_log; drop table mysql.general_log; -ERROR 42S02: Unknown table 'general_log' +ERROR 42S02: Unknown table 'mysql.general_log' drop table mysql.slow_log; -ERROR 42S02: Unknown table 'slow_log' +ERROR 42S02: Unknown table 'mysql.slow_log' use mysql; CREATE TABLE `general_log` ( -`event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP +`event_time` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `user_host` mediumtext NOT NULL, -`thread_id` int(11) NOT NULL, +`thread_id` BIGINT(21) UNSIGNED NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -281,7 +285,8 @@ ON UPDATE CURRENT_TIMESTAMP, `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, -`sql_text` mediumtext NOT NULL +`sql_text` mediumtext NOT NULL, +`thread_id` BIGINT(21) UNSIGNED NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log'; set global general_log='ON'; set global slow_query_log='ON'; @@ -308,7 +313,7 @@ event_time user_host thread_id server_id command_type argument TIMESTAMP USER_HOST THREAD_ID 1 Query select * from general_log truncate table slow_log; select * from slow_log; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id create table general_log_new like general_log; rename table general_log TO renamed_general_log, general_log_new TO general_log; create table slow_log_new like slow_log; @@ -329,9 +334,9 @@ TIMESTAMP USER_HOST THREAD_ID 1 Query select * from slow_log TIMESTAMP USER_HOST THREAD_ID 1 Query create table general_log_new like general_log TIMESTAMP USER_HOST THREAD_ID 1 Query rename table general_log TO renamed_general_log, general_log_new TO general_log select * from slow_log; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id select * from renamed_slow_log; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id set global general_log='OFF'; RENAME TABLE general_log TO general_log2; set global slow_query_log='OFF'; @@ -362,8 +367,6 @@ show tables like "%log%"; Tables_in_mysql (%log%) general_log general_log_new -ndb_binlog_index -slave_relay_log_info slow_log slow_log_new drop table slow_log_new, general_log_new; @@ -426,10 +429,10 @@ SELECT "My own slow query", sleep(2); My own slow query sleep(2) My own slow query 0 SELECT * FROM mysql.slow_log WHERE seq >= 2 LIMIT 3; -start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text seq -START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 2 -START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 3 -START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 4 +start_time user_host query_time lock_time rows_sent rows_examined db last_insert_id insert_id server_id sql_text thread_id seq +START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 3 2 +START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 3 3 +START_TIME USER_HOST QUERY_TIME 00:00:00.000000 1 0 test 0 0 1 SELECT "My own slow query", sleep(2) 3 4 SET GLOBAL slow_query_log = 0; SET SESSION long_query_time =@saved_long_query_time; FLUSH LOGS; @@ -548,6 +551,7 @@ BEGIN DECLARE start_time, query_time, lock_time CHAR(28); DECLARE user_host MEDIUMTEXT; DECLARE rows_set, rows_examined, last_insert_id, insert_id, server_id INT; +DECLARE thread_id BIGINT UNSIGNED; DECLARE dbname MEDIUMTEXT; DECLARE sql_text BLOB; DECLARE done INT DEFAULT 0; @@ -561,14 +565,14 @@ DECLARE CONTINUE HANDLER FOR ER_SP_FETCH_NO_DATA SET done = 1; FETCH cur1 INTO start_time, user_host, query_time, lock_time, rows_set, rows_examined, dbname, last_insert_id, -insert_id, server_id, sql_text; +insert_id, server_id, sql_text, thread_id; END; IF NOT done THEN BEGIN INSERT INTO `db_17876.slow_log_data` VALUES(start_time, user_host, query_time, lock_time, rows_set, rows_examined, -dbname, last_insert_id, insert_id, server_id, sql_text); +dbname, last_insert_id, insert_id, server_id, sql_text, thread_id); END; END IF; END; diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result index 5732b94a90c..5a53ca03736 100644 --- a/mysql-test/r/log_tables_upgrade.result +++ b/mysql-test/r/log_tables_upgrade.result @@ -33,16 +33,12 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.renamed_general_log OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK diff --git a/mysql-test/r/lowercase_table4.result b/mysql-test/r/lowercase_table4.result index aa81eff5194..02e2012a186 100644 --- a/mysql-test/r/lowercase_table4.result +++ b/mysql-test/r/lowercase_table4.result @@ -28,18 +28,7 @@ Create Table CREATE TABLE `Table2` ( KEY `fk1` (`c2`), CONSTRAINT `fk1` FOREIGN KEY (`c2`) REFERENCES `Table1` (`c1`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; -CONSTRAINT_CATALOG def -CONSTRAINT_SCHEMA mysql -CONSTRAINT_NAME innodb_index_stats_ibfk_1 -UNIQUE_CONSTRAINT_CATALOG def -UNIQUE_CONSTRAINT_SCHEMA mysql -UNIQUE_CONSTRAINT_NAME PRIMARY -MATCH_OPTION NONE -UPDATE_RULE RESTRICT -DELETE_RULE RESTRICT -TABLE_NAME innodb_index_stats -REFERENCED_TABLE_NAME innodb_table_stats +SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='test'; CONSTRAINT_CATALOG def CONSTRAINT_SCHEMA test CONSTRAINT_NAME fk1 @@ -98,18 +87,7 @@ Create Table CREATE TABLE `Customer` ( `Id` int(11) NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 -SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS; -CONSTRAINT_CATALOG def -CONSTRAINT_SCHEMA mysql -CONSTRAINT_NAME innodb_index_stats_ibfk_1 -UNIQUE_CONSTRAINT_CATALOG def -UNIQUE_CONSTRAINT_SCHEMA mysql -UNIQUE_CONSTRAINT_NAME PRIMARY -MATCH_OPTION NONE -UPDATE_RULE RESTRICT -DELETE_RULE RESTRICT -TABLE_NAME innodb_index_stats -REFERENCED_TABLE_NAME innodb_table_stats +SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='test'; CONSTRAINT_CATALOG def CONSTRAINT_SCHEMA test CONSTRAINT_NAME product_order_ibfk_1 diff --git a/mysql-test/r/mdl_sync.result b/mysql-test/r/mdl_sync.result index 1c94f867a54..d71498f90dd 100644 --- a/mysql-test/r/mdl_sync.result +++ b/mysql-test/r/mdl_sync.result @@ -5,7 +5,7 @@ create table t2 (i int); connection: default lock tables t2 read; connection: con1 -set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +set debug_sync='mdl_upgrade_lock SIGNAL parked WAIT_FOR go'; alter table t1 rename t3; connection: default set debug_sync= 'now WAIT_FOR parked'; @@ -16,7 +16,7 @@ connection: con1 connection: default unlock tables; connection: con2 -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' drop table t3; SET DEBUG_SYNC= 'RESET'; # @@ -48,8 +48,13 @@ select count(*) from t1; count(*) 0 insert into t1 values (1), (1); +# Check that SU lock is compatible with it. To do this use ALTER TABLE +# which will fail when constructing .frm and thus obtaining SU metadata +# lock. +alter table t1 add index (not_exist); +ERROR 42000: Key column 'not_exist' doesn't exist in table # Check that SNW lock is compatible with it. To do this use ALTER TABLE -# which will fail after opening the table and thus obtaining SNW metadata +# which will fail during copying the table and thus obtaining SNW metadata # lock. alter table t1 add primary key (c1); ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -139,8 +144,13 @@ select count(*) from t1; count(*) 3 insert into t1 values (1); +# Check that SU lock is compatible with it. To do this use ALTER TABLE +# which will fail when constructing .frm and thus obtaining SU metadata +# lock. +alter table t1 add index (not_exist); +ERROR 42000: Key column 'not_exist' doesn't exist in table # Check that SNW lock is compatible with it. To do this use ALTER TABLE -# which will fail after opening the table and thus obtaining SNW metadata +# which will fail during copying the table and thus obtaining SNW metadata # lock. alter table t1 add primary key (c1); ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -244,8 +254,13 @@ select count(*) from t1; count(*) 3 insert into t1 values (1); +# Check that SU lock is compatible with it. To do this use ALTER TABLE +# which will fail when constructing .frm and thus obtaining SU metadata +# lock. +alter table t1 add index (not_exist); +ERROR 42000: Key column 'not_exist' doesn't exist in table # Check that SNW lock is compatible with it. To do this use ALTER TABLE -# which will fail after opening the table and thus obtaining SNW metadata +# which will fail during copying the table and thus obtaining SNW metadata # lock. alter table t1 add primary key (c1); ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -334,8 +349,13 @@ c1 # effects of concurrent insert. select * from t1; insert into t1 values (1); +# Check that SU lock is compatible with it. To do this use ALTER TABLE +# which will fail when constructing .frm and thus obtaining SU metadata +# lock. +alter table t1 add index (not_exist); +ERROR 42000: Key column 'not_exist' doesn't exist in table # Check that SNW lock is not compatible with SW lock. -# Again we use ALTER TABLE which fails after opening +# Again we use ALTER TABLE which fails during copying # the table to avoid upgrade of SNW -> X. # Sending: alter table t1 add primary key (c1);; @@ -397,15 +417,111 @@ rename table t2 to t1; # Switching to connection 'default'. # # -# 5) Acquire SNW lock on the table. We have to use DEBUG_SYNC for -# this, to prevent SNW from being immediately upgraded to X. +# 5) Acquire SU lock on the table. We have to use DEBUG_SYNC for +# this, to prevent SU from being immediately upgraded to X. # -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_opened_table SIGNAL locked WAIT_FOR finish'; # Sending: alter table t1 add primary key (c1);; # # Switching to connection 'mdl_con1'. set debug_sync= 'now WAIT_FOR locked'; +# Check that S, SH, SR and SW locks are compatible with it. +handler t1 open; +handler t1 close; +select column_name from information_schema.columns where +table_schema='test' and table_name='t1'; +column_name +c1 +select count(*) from t1; +count(*) +5 +delete from t1 limit 1; +# Check that SU lock is incompatible with SU lock. +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con2'. +# Check that the above ALTER is blocked because of SU lock. +# Unblock ALTERs. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping first ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping another ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'default'. +set debug_sync= 'alter_opened_table SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that SNRW lock is incompatible with SU lock. +# Sending: +lock table t1 write;; +# +# Switching to connection 'mdl_con2'. +# Check that the above LOCK TABLES is blocked because of SU lock. +# Unblock ALTER and thus LOCK TABLES. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Reaping ALTER TABLE. +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +# +# Switching to connection 'mdl_con1'. +# Reaping LOCK TABLES +insert into t1 values (1); +unlock tables; +# +# Switching to connection 'default'. +set debug_sync= 'alter_opened_table SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1);; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; +# Check that X lock is incompatible with SU lock. +# Sending: +rename table t1 to t2;; +# +# Switching to connection 'mdl_con2'. +# Check that the above RENAME is blocked because of SU lock. +# Unblock ALTER and thus RENAME TABLE. +set debug_sync= 'now SIGNAL finish'; +# +# Switching to connection 'default'. +# Now we have ALTER TABLE with SU->SNW and RENAME TABLE with pending +# X-lock. In this case ALTER TABLE should be chosen as victim. +# Reaping ALTER TABLE. +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# +# Switching to connection 'mdl_con1'. +# Reaping RENAME TABLE +# Revert back to original state of things. +rename table t2 to t1; +# +# There is no need to check that upgrade from SNW/SNRW to X is +# blocked by presence of another SU lock because SNW/SNRW is +# incompatible with SU anyway. +# +# Switching to connection 'default'. +# +# +# 6) Acquire SNW lock on the table. We have to use DEBUG_SYNC for +# this, to prevent SNW from being immediately upgraded to X. +# +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; +# Sending: +alter table t1 add primary key (c1), lock=shared, algorithm=copy;; +# +# Switching to connection 'mdl_con1'. +set debug_sync= 'now WAIT_FOR locked'; # Check that S, SH and SR locks are compatible with it. handler t1 open; handler t1 close; @@ -433,13 +549,13 @@ ERROR 23000: Duplicate entry '1' for key 'PRIMARY' # Reaping DELETE. # # Switching to connection 'default'. -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; # Sending: -alter table t1 add primary key (c1);; +alter table t1 add primary key (c1), lock=shared, algorithm=copy;; # # Switching to connection 'mdl_con1'. set debug_sync= 'now WAIT_FOR locked'; -# Check that SNW lock is incompatible with SNW lock. +# Check that SU lock is incompatible with SNW lock. # Sending: alter table t1 add primary key (c1);; # @@ -456,10 +572,14 @@ ERROR 23000: Duplicate entry '1' for key 'PRIMARY' # Reaping another ALTER TABLE. ERROR 23000: Duplicate entry '1' for key 'PRIMARY' # +# Note that we can't easily check SNW vs SNW locks since +# SNW is only used by ALTER TABLE after upgrading from SU +# and SU is also incompatible with SNW. +# # Switching to connection 'default'. -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; # Sending: -alter table t1 add primary key (c1);; +alter table t1 add primary key (c1), lock=shared, algorithm=copy;; # # Switching to connection 'mdl_con1'. set debug_sync= 'now WAIT_FOR locked'; @@ -482,9 +602,9 @@ insert into t1 values (1); unlock tables; # # Switching to connection 'default'. -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; # Sending: -alter table t1 add primary key (c1);; +alter table t1 add primary key (c1), algorithm=copy, lock=shared;; # # Switching to connection 'mdl_con1'. set debug_sync= 'now WAIT_FOR locked'; @@ -513,7 +633,7 @@ rename table t2 to t1; # Switching to connection 'default'. # # -# 6) Acquire SNRW lock on the table. +# 7) Acquire SNRW lock on the table. # # lock table t1 write; @@ -560,12 +680,12 @@ unlock tables; lock table t1 write; # # Switching to connection 'mdl_con1'. -# Check that SNW lock is incompatible with SNRW lock. +# Check that SU lock is incompatible with SNRW lock. # Sending: alter table t1 add primary key (c1);; # # Switching to connection 'default'. -# Check that the above ALTER is blocked because of UNWR lock. +# Check that the above ALTER is blocked because of SNRW lock. # Unblock ALTER. unlock tables; # @@ -573,6 +693,10 @@ unlock tables; # Reaping ALTER TABLE. ERROR 23000: Duplicate entry '1' for key 'PRIMARY' # +# Note that we can't easily check SNW vs SNRW locks since +# SNW is only used by ALTER TABLE after upgrading from SU +# and SU is also incompatible with SNRW. +# # Switching to connection 'default'. lock table t1 write; # @@ -616,7 +740,7 @@ rename table t2 to t1; # Switching to connection 'default'. # # -# 7) Now do the same round of tests for X lock. We use additional +# 8) Now do the same round of tests for X lock. We use additional # table to get long-lived lock of this type. # create table t2 (c1 int); @@ -744,7 +868,7 @@ rename table t1 to t2;; # # Switching to connection 'mdl_con1'. # Check that RENAME has acquired X lock on t1 and is waiting for t2. -# Check that SNW lock is incompatible with X lock. +# Check that SU lock is incompatible with X lock. # Sending: alter table t1 add primary key (c1);; # @@ -761,7 +885,11 @@ ERROR 42S01: Table 't2' already exists # Switching to connection 'mdl_con1'. # Reaping ALTER. ERROR 23000: Duplicate entry '1' for key 'PRIMARY' -# +# +# Note that we can't easily check SNW vs X locks since +# SNW is only used by ALTER TABLE after upgrading from SU +# and SU is also incompatible with X. +# # Switching to connection 'mdl_con2'. # Prepare for blocking RENAME TABLE. lock tables t2 read; @@ -822,6 +950,9 @@ rename table t3 to t1; # are pending. I.e. let us test rules for priorities between # different types of metadata locks. # +# Note: No tests for pending SU lock as this lock requires +# even stronger active or pending lock. +# # # Switching to connection 'mdl_con2'. # @@ -1138,6 +1269,9 @@ unlock tables; # transactional context. Obviously we are mostly interested # in conflicting types of locks. # +# Note: No tests for active/pending SU lock since +# ALTER TABLE is in its own transaction. +# # # 1) Let us check how various locks used within transactional # context interact with active/pending SNW lock. @@ -1154,9 +1288,9 @@ count(*) # We have to use DEBUG_SYNC facility as otherwise SNW lock # will be immediately released (or upgraded to X lock). insert into t2 values (1), (1); -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; # Sending: -alter table t2 add primary key (c1);; +alter table t2 add primary key (c1), algorithm=copy, lock=shared;; # # Switching to connection 'default'. set debug_sync= 'now WAIT_FOR locked'; @@ -1199,9 +1333,9 @@ count(*) # # Switching to connection 'mdl_con1'. # Create an active SNW lock on t1. -set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL locked WAIT_FOR finish'; # Sending: -alter table t1 add primary key (c1);; +alter table t1 add primary key (c1), algorithm=copy, lock=shared;; # # Switching to connection 'default'. set debug_sync= 'now WAIT_FOR locked'; @@ -1986,7 +2120,7 @@ drop tables t1, t2; # create table t1 (i int); # Ensure that ALTER waits once it has acquired SNW lock. -set debug_sync='after_open_table_mdl_shared SIGNAL parked1 WAIT_FOR go1'; +set debug_sync='alter_table_copy_after_lock_upgrade SIGNAL parked1 WAIT_FOR go1'; # Sending: alter table t1 add column j int; # @@ -2293,13 +2427,18 @@ set global log_output=@save_log_output; # drop tables if exists t1, t2; create table t1 (i int); +insert into t1 values(1); # Let us check that we won't deadlock if during filling # of I_S table we encounter conflicting metadata lock # which owner is in its turn waiting for our connection. lock tables t1 read; -# Switching to connection 'con46044'. +# Switching to connection 'con46044_2'. # Sending: -create table t2 select * from t1 for update;; +update t1 set i = 2; +# Switching to connection 'con46044'. +# Waiting until UPDATE t1 SET ... is blocked. +# Sending: +create table t2 select * from t1;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # First let us check that SHOW FIELDS/DESCRIBE doesn't @@ -2329,6 +2468,7 @@ unlock tables; # Switching to connection 'con46044'. # Reaping CREATE TABLE ... SELECT ... . drop table t2; +# Reaping UPDATE t1 statement # # Let us also check that queries to I_S wait for conflicting metadata # locks to go away instead of skipping table with a warning in cases @@ -2338,9 +2478,13 @@ drop table t2; # We check same three queries to I_S in this new situation. # Switching to connection 'con46044_2'. lock tables t1 read; -# Switching to connection 'con46044'. +# Switching to connection 'con46044_3'. # Sending: -create table t2 select * from t1 for update;; +update t1 set i = 3; +# Switching to connection 'con46044'. +# Waiting until UPDATE t1 SET ... is blocked. +# Sending: +create table t2 select * from t1;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Let us check that SHOW FIELDS/DESCRIBE gets blocked. @@ -2356,11 +2500,16 @@ unlock tables; Field Type Null Key Default Extra i int(11) YES NULL drop table t2; +# Reaping UPDATE t1 statement # Switching to connection 'con46044_2'. lock tables t1 read; -# Switching to connection 'con46044'. +# Switching to connection 'con46044_3'. # Sending: -create table t2 select * from t1 for update;; +update t1 set i = 4; +# Switching to connection 'con46044'. +# Waiting until UPDATE t1 SET ... is blocked. +# Sending: +create table t2 select * from t1;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Check that I_S query which reads only .FRMs gets blocked. @@ -2376,11 +2525,16 @@ unlock tables; column_name i drop table t2; +# Reaping UPDATE t1 statement # Switching to connection 'con46044_2'. lock tables t1 read; -# Switching to connection 'con46044'. +# Switching to connection 'con46044_3'. # Sending: -create table t2 select * from t1 for update;; +update t1 set i = 5; +# Switching to connection 'con46044'. +# Waiting until UPDATE t1 SET ... is blocked. +# Sending: +create table t2 select * from t1;; # Switching to connection 'default'. # Waiting until CREATE TABLE ... SELECT ... is blocked. # Finally, check that I_S query which does full-blown table open @@ -2397,6 +2551,7 @@ unlock tables; table_name table_type auto_increment table_comment t2 BASE TABLE NULL drop table t2; +# Reaping UPDATE t1 statement # Switching to connection 'default'. # Clean-up. drop table t1; @@ -2414,7 +2569,7 @@ c1 c2 c3 3 3 0 # # Switching to connection 'con46273'. -set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR alter_go'; +set debug_sync='alter_table_copy_after_lock_upgrade SIGNAL alter_table_locked WAIT_FOR alter_go'; alter table t1 add column e int, rename to t2;; # # Switching to connection 'default'. @@ -2558,9 +2713,9 @@ drop table if exists t1; set debug_sync= 'RESET'; create table t1 (i int) engine=InnoDB; # Switching to connection 'con50913_1'. -set debug_sync= 'thr_multi_lock_after_thr_lock SIGNAL parked WAIT_FOR go'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL parked WAIT_FOR go'; # Sending: -alter table t1 add column j int; +alter table t1 add column j int, ALGORITHM=COPY; # Switching to connection 'default'. # Wait until ALTER TABLE gets blocked on a sync point after # acquiring thr_lock.c lock. @@ -2600,7 +2755,7 @@ i # Switching to connection 'default'. # Start ALTER TABLE which will acquire SNW lock and # table lock and get blocked on sync point. -set debug_sync= 'thr_multi_lock_after_thr_lock SIGNAL parked WAIT_FOR go'; +set debug_sync= 'alter_table_copy_after_lock_upgrade SIGNAL parked WAIT_FOR go'; # Sending: alter table t1 add column j int; # Switching to connection 'con1'. @@ -2889,7 +3044,7 @@ SET DEBUG_SYNC= 'now SIGNAL blocked'; # Reaping: DROP DATABASE db1 # Connection con2 # Reaping: DROP TABLE db1.t1 -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'db1.t1' # Connection default SET DEBUG_SYNC= 'RESET'; # @@ -2934,12 +3089,16 @@ CREATE TABLE m1(a INT) engine=MERGE UNION=(t1, t2); INSERT INTO t1 VALUES (1), (2); INSERT INTO t2 VALUES (3), (4); # Connection con1 -SET DEBUG_SYNC= 'mdl_upgrade_shared_lock_to_exclusive SIGNAL upgrade WAIT_FOR continue'; +# We need EXECUTE 2 since ALTER TABLE does SU => SNW => X and we want +# to stop at the second upgrade. +SET DEBUG_SYNC= 'mdl_upgrade_lock SIGNAL upgrade WAIT_FOR continue EXECUTE 2'; # Sending: ALTER TABLE m1 engine=MERGE UNION=(t2, t1); # Connection con2 # Waiting for ALTER TABLE to try lock upgrade SET DEBUG_SYNC= 'now WAIT_FOR upgrade'; +SET DEBUG_SYNC= 'now SIGNAL continue'; +SET DEBUG_SYNC= 'now WAIT_FOR upgrade'; # Sending: DELETE FROM t2 WHERE a = 3; # Connection default diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 5c4261e7d6d..ff75f653368 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -2883,7 +2883,7 @@ Table Create Table m2 CREATE TEMPORARY TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m2; c1 c2 111 121 @@ -2900,7 +2900,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m1; c1 c2 111 121 @@ -2989,7 +2989,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO m1 VALUES (511, 521); SELECT * FROM m1; c1 c2 @@ -3040,7 +3040,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) # CREATE TABLE m2 SELECT * FROM m1; SHOW CREATE TABLE m2; @@ -3092,7 +3092,7 @@ Table Create Table m2 CREATE TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m2; c1 c2 111 121 @@ -3118,7 +3118,7 @@ Table Create Table m2 CREATE TEMPORARY TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m2; c1 c2 111 121 @@ -3288,7 +3288,7 @@ Table Create Table m2 CREATE TEMPORARY TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m2; c1 c2 111 121 @@ -3305,7 +3305,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m1; c1 c2 111 121 @@ -3396,7 +3396,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO m1 VALUES (511, 521); SELECT * FROM m1; c1 c2 @@ -3447,7 +3447,7 @@ Table Create Table m1 CREATE TEMPORARY TABLE `m1` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) CREATE TABLE m2 SELECT * FROM m1; ERROR HY000: Table 'm2' was not locked with LOCK TABLES # @@ -3492,14 +3492,14 @@ Table Create Table m2 CREATE TEMPORARY TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) LOCK TABLE m1 WRITE, m2 WRITE; SHOW CREATE TABLE m2; Table Create Table m2 CREATE TEMPORARY TABLE `m2` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL -) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) +) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 INSERT_METHOD=LAST UNION=(`t1`,`t2`) SELECT * FROM m2; c1 c2 111 121 diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index 3c67889ce10..d974080d99b 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -695,7 +695,7 @@ DROP TABLE t1; # DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 ( id int(10) unsigned NOT NULL, level tinyint(3) unsigned NOT NULL, @@ -704,7 +704,7 @@ PRIMARY KEY (id) INSERT INTO t1 VALUES (2519583,1); DROP TABLE IF EXISTS t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t2 ( club_id int(11) NOT NULL DEFAULT '0', profile_id int(11) NOT NULL DEFAULT '0', @@ -714,7 +714,7 @@ PRIMARY KEY (profile_id,club_id) INSERT INTO t2 VALUES (2,2519583,12); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( member_level_id int(11) unsigned NOT NULL DEFAULT '0', map_level int(11) unsigned NOT NULL DEFAULT '0', diff --git a/mysql-test/r/myisam-system.result b/mysql-test/r/myisam-system.result index 9d5a59459ec..af5de8f2749 100644 --- a/mysql-test/r/myisam-system.result +++ b/mysql-test/r/myisam-system.result @@ -16,4 +16,4 @@ drop table t1; Warnings: Warning 2 Can't find file: './test/t1.MYI' (errno: 2 "No such file or directory") drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index 267110be487..5b0dbbf6957 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -454,7 +454,7 @@ a b c drop table t1; DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1 (a varchar(150) NOT NULL, KEY (a)); INSERT t1 VALUES ("can \tcan"); INSERT t1 VALUES ("can can"); diff --git a/mysql-test/r/mysql5613mysql.result b/mysql-test/r/mysql5613mysql.result new file mode 100644 index 00000000000..501c723f711 --- /dev/null +++ b/mysql-test/r/mysql5613mysql.result @@ -0,0 +1,302 @@ +# +# MDEV-4819 Upgrade from MySQL 5.6 does not work +# Testing that we can open system tables created in MySQL-5.6 (5.6.13) +# +SHOW CREATE TABLE columns_priv; +Table Create Table +columns_priv CREATE TABLE `columns_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Column_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '', + PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges' +SELECT * FROM columns_priv LIMIT 0; +Host Db User Table_name Column_name Timestamp Column_priv +DROP TABLE columns_priv; +SHOW CREATE TABLE db; +Table Create Table +db CREATE TABLE `db` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + PRIMARY KEY (`Host`,`Db`,`User`), + KEY `User` (`User`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges' +SELECT * FROM db LIMIT 0; +Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv Trigger_priv +DROP TABLE db; +SHOW CREATE TABLE event; +Table Create Table +event CREATE TABLE `event` ( + `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `name` char(64) NOT NULL DEFAULT '', + `body` longblob NOT NULL, + `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `execute_at` datetime DEFAULT NULL, + `interval_value` int(11) DEFAULT NULL, + `interval_field` enum('YEAR','QUARTER','MONTH','DAY','HOUR','MINUTE','WEEK','SECOND','MICROSECOND','YEAR_MONTH','DAY_HOUR','DAY_MINUTE','DAY_SECOND','HOUR_MINUTE','HOUR_SECOND','MINUTE_SECOND','DAY_MICROSECOND','HOUR_MICROSECOND','MINUTE_MICROSECOND','SECOND_MICROSECOND') DEFAULT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `last_executed` datetime DEFAULT NULL, + `starts` datetime DEFAULT NULL, + `ends` datetime DEFAULT NULL, + `status` enum('ENABLED','DISABLED','SLAVESIDE_DISABLED') NOT NULL DEFAULT 'ENABLED', + `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '', + `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `originator` int(10) unsigned NOT NULL, + `time_zone` char(64) CHARACTER SET latin1 NOT NULL DEFAULT 'SYSTEM', + `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `body_utf8` longblob, + PRIMARY KEY (`db`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events' +SELECT * FROM event LIMIT 0; +db name body definer execute_at interval_value interval_field created modified last_executed starts ends status on_completion sql_mode comment originator time_zone character_set_client collation_connection db_collation body_utf8 +DROP TABLE event; +SHOW CREATE TABLE func; +Table Create Table +func CREATE TABLE `func` ( + `name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `ret` tinyint(1) NOT NULL DEFAULT '0', + `dl` char(128) COLLATE utf8_bin NOT NULL DEFAULT '', + `type` enum('function','aggregate') CHARACTER SET utf8 NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='User defined functions' +SELECT * FROM func LIMIT 0; +name ret dl type +DROP TABLE func; +SHOW CREATE TABLE plugin; +Table Create Table +plugin CREATE TABLE `plugin` ( + `name` varchar(64) NOT NULL DEFAULT '', + `dl` varchar(128) NOT NULL DEFAULT '', + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='MySQL plugins' +SELECT * FROM plugin LIMIT 0; +name dl +DROP TABLE plugin; +SHOW CREATE TABLE proc; +Table Create Table +proc CREATE TABLE `proc` ( + `db` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `name` char(64) NOT NULL DEFAULT '', + `type` enum('FUNCTION','PROCEDURE') NOT NULL, + `specific_name` char(64) NOT NULL DEFAULT '', + `language` enum('SQL') NOT NULL DEFAULT 'SQL', + `sql_data_access` enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') NOT NULL DEFAULT 'CONTAINS_SQL', + `is_deterministic` enum('YES','NO') NOT NULL DEFAULT 'NO', + `security_type` enum('INVOKER','DEFINER') NOT NULL DEFAULT 'DEFINER', + `param_list` blob NOT NULL, + `returns` longblob NOT NULL, + `body` longblob NOT NULL, + `definer` char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', + `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE','NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH') NOT NULL DEFAULT '', + `comment` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `character_set_client` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `collation_connection` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `db_collation` char(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, + `body_utf8` longblob, + PRIMARY KEY (`db`,`name`,`type`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Stored Procedures' +SELECT * FROM proc LIMIT 0; +db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment character_set_client collation_connection db_collation body_utf8 +DROP TABLE proc; +SHOW CREATE TABLE procs_priv; +Table Create Table +procs_priv CREATE TABLE `procs_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Routine_name` char(64) CHARACTER SET utf8 NOT NULL DEFAULT '', + `Routine_type` enum('FUNCTION','PROCEDURE') COLLATE utf8_bin NOT NULL, + `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '', + `Proc_priv` set('Execute','Alter Routine','Grant') CHARACTER SET utf8 NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`,`Routine_type`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges' +SELECT * FROM procs_priv LIMIT 0; +Host Db User Routine_name Routine_type Grantor Proc_priv Timestamp +DROP TABLE procs_priv; +SHOW CREATE TABLE proxies_priv; +Table Create Table +proxies_priv CREATE TABLE `proxies_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Proxied_host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Proxied_user` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `With_grant` tinyint(1) NOT NULL DEFAULT '0', + `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Host`,`User`,`Proxied_host`,`Proxied_user`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='User proxy privileges' +SELECT * FROM proxies_priv LIMIT 0; +Host User Proxied_host Proxied_user With_grant Grantor Timestamp +DROP TABLE proxies_priv; +SHOW CREATE TABLE servers; +Table Create Table +servers CREATE TABLE `servers` ( + `Server_name` char(64) NOT NULL DEFAULT '', + `Host` char(64) NOT NULL DEFAULT '', + `Db` char(64) NOT NULL DEFAULT '', + `Username` char(64) NOT NULL DEFAULT '', + `Password` char(64) NOT NULL DEFAULT '', + `Port` int(4) NOT NULL DEFAULT '0', + `Socket` char(64) NOT NULL DEFAULT '', + `Wrapper` char(64) NOT NULL DEFAULT '', + `Owner` char(64) NOT NULL DEFAULT '', + PRIMARY KEY (`Server_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='MySQL Foreign Servers table' +SELECT * FROM servers LIMIT 0; +Server_name Host Db Username Password Port Socket Wrapper Owner +DROP TABLE servers; +SHOW CREATE TABLE tables_priv; +Table Create Table +tables_priv CREATE TABLE `tables_priv` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `Db` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Table_name` char(64) COLLATE utf8_bin NOT NULL DEFAULT '', + `Grantor` char(77) COLLATE utf8_bin NOT NULL DEFAULT '', + `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `Table_priv` set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') CHARACTER SET utf8 NOT NULL DEFAULT '', + `Column_priv` set('Select','Insert','Update','References') CHARACTER SET utf8 NOT NULL DEFAULT '', + PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Table privileges' +SELECT * FROM tables_priv LIMIT 0; +Host Db User Table_name Grantor Timestamp Table_priv Column_priv +DROP TABLE tables_priv; +SHOW CREATE TABLE time_zone_leap_second; +Table Create Table +time_zone_leap_second CREATE TABLE `time_zone_leap_second` ( + `Transition_time` bigint(20) NOT NULL, + `Correction` int(11) NOT NULL, + PRIMARY KEY (`Transition_time`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Leap seconds information for time zones' +SELECT * FROM time_zone_leap_second LIMIT 0; +Transition_time Correction +DROP TABLE time_zone_leap_second; +SHOW CREATE TABLE time_zone; +Table Create Table +time_zone CREATE TABLE `time_zone` ( + `Time_zone_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `Use_leap_seconds` enum('Y','N') NOT NULL DEFAULT 'N', + PRIMARY KEY (`Time_zone_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zones' +SELECT * FROM time_zone LIMIT 0; +Time_zone_id Use_leap_seconds +DROP TABLE time_zone; +SHOW CREATE TABLE time_zone_name; +Table Create Table +time_zone_name CREATE TABLE `time_zone_name` ( + `Name` char(64) NOT NULL, + `Time_zone_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`Name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone names' +SELECT * FROM time_zone_name LIMIT 0; +Name Time_zone_id +DROP TABLE time_zone_name; +SHOW CREATE TABLE time_zone_transition; +Table Create Table +time_zone_transition CREATE TABLE `time_zone_transition` ( + `Time_zone_id` int(10) unsigned NOT NULL, + `Transition_time` bigint(20) NOT NULL, + `Transition_type_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`Time_zone_id`,`Transition_time`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transitions' +SELECT * FROM time_zone_transition LIMIT 0; +Time_zone_id Transition_time Transition_type_id +DROP TABLE time_zone_transition; +SHOW CREATE TABLE time_zone_transition_type; +Table Create Table +time_zone_transition_type CREATE TABLE `time_zone_transition_type` ( + `Time_zone_id` int(10) unsigned NOT NULL, + `Transition_type_id` int(10) unsigned NOT NULL, + `Offset` int(11) NOT NULL DEFAULT '0', + `Is_DST` tinyint(3) unsigned NOT NULL DEFAULT '0', + `Abbreviation` char(8) NOT NULL DEFAULT '', + PRIMARY KEY (`Time_zone_id`,`Transition_type_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Time zone transition types' +SELECT * FROM time_zone_transition_type LIMIT 0; +Time_zone_id Transition_type_id Offset Is_DST Abbreviation +DROP TABLE time_zone_transition_type; +SHOW CREATE TABLE user; +Table Create Table +user CREATE TABLE `user` ( + `Host` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `User` char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + `Password` char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', + `Select_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Insert_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Update_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Delete_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Drop_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Reload_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Shutdown_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Process_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `File_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Grant_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Index_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_db_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Super_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Execute_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Repl_slave_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Repl_client_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_user_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `Create_tablespace_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + `ssl_type` enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '', + `ssl_cipher` blob NOT NULL, + `x509_issuer` blob NOT NULL, + `x509_subject` blob NOT NULL, + `max_questions` int(11) unsigned NOT NULL DEFAULT '0', + `max_updates` int(11) unsigned NOT NULL DEFAULT '0', + `max_connections` int(11) unsigned NOT NULL DEFAULT '0', + `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0', + `plugin` char(64) COLLATE utf8_bin DEFAULT '', + `authentication_string` text COLLATE utf8_bin, + `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + PRIMARY KEY (`Host`,`User`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' +SELECT * FROM user LIMIT 0; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired +DROP TABLE user; diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result index a08e6f63fb4..74832162afb 100644 --- a/mysql-test/r/mysql_upgrade.result +++ b/mysql-test/r/mysql_upgrade.result @@ -21,15 +21,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -67,15 +63,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -113,15 +105,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -161,15 +149,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -213,15 +197,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -268,15 +248,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -326,15 +302,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK diff --git a/mysql-test/r/mysql_upgrade_ssl.result b/mysql-test/r/mysql_upgrade_ssl.result index 60bf427cef6..d0609deb552 100644 --- a/mysql-test/r/mysql_upgrade_ssl.result +++ b/mysql-test/r/mysql_upgrade_ssl.result @@ -23,15 +23,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index 17dc9ad9a35..ce9bf367945 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -17,15 +17,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -54,15 +50,11 @@ status : OK mysql.innodb_table_stats note : Table does not support optimize, doing recreate + analyze instead status : OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -85,15 +77,11 @@ mysql.host OK mysql.index_stats OK mysql.innodb_index_stats OK mysql.innodb_table_stats OK -mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.servers OK -mysql.slave_master_info OK -mysql.slave_relay_log_info OK -mysql.slave_worker_info OK mysql.table_stats OK mysql.tables_priv OK mysql.time_zone OK @@ -120,15 +108,11 @@ status : OK mysql.innodb_table_stats note : Table does not support optimize, doing recreate + analyze instead status : OK -mysql.ndb_binlog_index Table is already up to date mysql.plugin Table is already up to date mysql.proc Table is already up to date mysql.procs_priv Table is already up to date mysql.proxies_priv Table is already up to date mysql.servers Table is already up to date -mysql.slave_master_info Table is already up to date -mysql.slave_relay_log_info Table is already up to date -mysql.slave_worker_info Table is already up to date mysql.table_stats Table is already up to date mysql.tables_priv Table is already up to date mysql.time_zone Table is already up to date diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 5f8cf38ac09..610157502b9 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -217,6 +217,7 @@ The following options may be given as the first argument: Possible values are: SINGLE_PREC_HB - single precision height-balanced, DOUBLE_PREC_HB - double precision height-balanced. + --host-cache-size=# How many host names should be cached to avoid resolving. --ignore-builtin-innodb Disable initialization of builtin InnoDB plugin --ignore-db-dirs=name @@ -422,6 +423,8 @@ The following options may be given as the first argument: --memlock Lock mysqld in memory. --metadata-locks-cache-size=# Size of unused metadata locks cache + --metadata-locks-hash-instances=# + Number of metadata locks hash instances --min-examined-row-limit=# Don't write queries to slow log that examine fewer rows than that @@ -535,8 +538,10 @@ The following options may be given as the first argument: record samples --performance-schema Enable the performance schema. + (Defaults to on; use --skip-performance-schema to disable.) --performance-schema-accounts-size=# - Maximum number of instrumented user@host accounts. + Maximum number of instrumented user@host accounts. Use 0 + to disable, -1 for automated sizing. --performance-schema-consumer-events-stages-current Default startup value for the events_stages_current consumer. @@ -577,64 +582,84 @@ The following options may be given as the first argument: consumer. (Defaults to on; use --skip-performance-schema-consumer-thread-instrumentation to disable.) --performance-schema-digests-size=# - Size of the statement digest. + Size of the statement digest. Use 0 to disable, -1 for + automated sizing. --performance-schema-events-stages-history-long-size=# - Number of rows in EVENTS_STAGES_HISTORY_LONG. + Number of rows in EVENTS_STAGES_HISTORY_LONG. Use 0 to + disable, -1 for automated sizing. --performance-schema-events-stages-history-size=# - Number of rows per thread in EVENTS_STAGES_HISTORY. + Number of rows per thread in EVENTS_STAGES_HISTORY. Use 0 + to disable, -1 for automated sizing. --performance-schema-events-statements-history-long-size=# - Number of rows in EVENTS_STATEMENTS_HISTORY_LONG. + Number of rows in EVENTS_STATEMENTS_HISTORY_LONG. Use 0 + to disable, -1 for automated sizing. --performance-schema-events-statements-history-size=# Number of rows per thread in EVENTS_STATEMENTS_HISTORY. + Use 0 to disable, -1 for automated sizing. --performance-schema-events-waits-history-long-size=# - Number of rows in EVENTS_WAITS_HISTORY_LONG. + Number of rows in EVENTS_WAITS_HISTORY_LONG. Use 0 to + disable, -1 for automated sizing. --performance-schema-events-waits-history-size=# - Number of rows per thread in EVENTS_WAITS_HISTORY. + Number of rows per thread in EVENTS_WAITS_HISTORY. Use 0 + to disable, -1 for automated sizing. --performance-schema-hosts-size=# - Maximum number of instrumented hosts. + Maximum number of instrumented hosts. Use 0 to disable, + -1 for automated sizing. --performance-schema-instrument[=name] Default startup value for a performance schema instrument. --performance-schema-max-cond-classes=# Maximum number of condition instruments. --performance-schema-max-cond-instances=# - Maximum number of instrumented condition objects. + Maximum number of instrumented condition objects. Use 0 + to disable, -1 for automated sizing. --performance-schema-max-file-classes=# Maximum number of file instruments. --performance-schema-max-file-handles=# Maximum number of opened instrumented files. --performance-schema-max-file-instances=# - Maximum number of instrumented files. + Maximum number of instrumented files. Use 0 to disable, + -1 for automated sizing. --performance-schema-max-mutex-classes=# Maximum number of mutex instruments. --performance-schema-max-mutex-instances=# - Maximum number of instrumented MUTEX objects. + Maximum number of instrumented MUTEX objects. Use 0 to + disable, -1 for automated sizing. --performance-schema-max-rwlock-classes=# Maximum number of rwlock instruments. --performance-schema-max-rwlock-instances=# - Maximum number of instrumented RWLOCK objects. + Maximum number of instrumented RWLOCK objects. Use 0 to + disable, -1 for automated sizing. --performance-schema-max-socket-classes=# Maximum number of socket instruments. --performance-schema-max-socket-instances=# - Maximum number of opened instrumented sockets. + Maximum number of opened instrumented sockets. Use 0 to + disable, -1 for automated sizing. --performance-schema-max-stage-classes=# Maximum number of stage instruments. --performance-schema-max-statement-classes=# Maximum number of statement instruments. --performance-schema-max-table-handles=# - Maximum number of opened instrumented tables. + Maximum number of opened instrumented tables. Use 0 to + disable, -1 for automated sizing. --performance-schema-max-table-instances=# - Maximum number of instrumented tables. + Maximum number of instrumented tables. Use 0 to disable, + -1 for automated sizing. --performance-schema-max-thread-classes=# Maximum number of thread instruments. --performance-schema-max-thread-instances=# - Maximum number of instrumented threads. + Maximum number of instrumented threads. Use 0 to disable, + -1 for automated sizing. + --performance-schema-session-connect-attrs-size=# + Size of session attribute string buffer per thread. Use 0 + to disable, -1 for automated sizing. --performance-schema-setup-actors-size=# Maximum number of rows in SETUP_ACTORS. --performance-schema-setup-objects-size=# Maximum number of rows in SETUP_OBJECTS. --performance-schema-users-size=# - Maximum number of instrumented users. + Maximum number of instrumented users. Use 0 to disable, + -1 for automated sizing. --pid-file=name Pid file used by safe_mysqld --plugin-dir=name Directory for plugins --plugin-load=name Semicolon-separated list of plugins to load, where each @@ -1047,6 +1072,7 @@ gtid-strict-mode FALSE help TRUE histogram-size 0 histogram-type SINGLE_PREC_HB +host-cache-size 128 ignore-builtin-innodb FALSE ignore-db-dirs init-connect @@ -1096,7 +1122,7 @@ max-allowed-packet 1048576 max-binlog-cache-size 18446744073709547520 max-binlog-size 1073741824 max-binlog-stmt-cache-size 18446744073709547520 -max-connect-errors 10 +max-connect-errors 100 max-connections 151 max-delayed-threads 20 max-error-count 64 @@ -1114,6 +1140,7 @@ max-user-connections 0 max-write-lock-count 18446744073709551615 memlock FALSE metadata-locks-cache-size 1024 +metadata-locks-hash-instances 8 min-examined-row-limit 0 mrr-buffer-size 262144 multi-range-count 256 @@ -1139,8 +1166,8 @@ optimizer-search-depth 62 optimizer-selectivity-sampling-limit 100 optimizer-switch index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,index_merge_sort_intersection=off,engine_condition_pushdown=off,index_condition_pushdown=on,derived_merge=on,derived_with_keys=on,firstmatch=on,loosescan=on,materialization=on,in_to_exists=on,semijoin=on,partial_match_rowid_merge=on,partial_match_table_scan=on,subquery_cache=on,mrr=off,mrr_cost_based=off,mrr_sort_keys=off,outer_join_with_cache=on,semijoin_with_cache=on,join_cache_incremental=on,join_cache_hashed=on,join_cache_bka=on,optimize_join_buffer_size=off,table_elimination=on optimizer-use-condition-selectivity 1 -performance-schema FALSE -performance-schema-accounts-size 100 +performance-schema TRUE +performance-schema-accounts-size 10 performance-schema-consumer-events-stages-current FALSE performance-schema-consumer-events-stages-history FALSE performance-schema-consumer-events-stages-history-long FALSE @@ -1153,35 +1180,36 @@ performance-schema-consumer-events-waits-history-long FALSE performance-schema-consumer-global-instrumentation TRUE performance-schema-consumer-statements-digest TRUE performance-schema-consumer-thread-instrumentation TRUE -performance-schema-digests-size 200 -performance-schema-events-stages-history-long-size 10000 -performance-schema-events-stages-history-size 10 -performance-schema-events-statements-history-long-size 10000 -performance-schema-events-statements-history-size 10 -performance-schema-events-waits-history-long-size 10000 -performance-schema-events-waits-history-size 10 -performance-schema-hosts-size 100 +performance-schema-digests-size 1000 +performance-schema-events-stages-history-long-size 100 +performance-schema-events-stages-history-size 5 +performance-schema-events-statements-history-long-size 100 +performance-schema-events-statements-history-size 5 +performance-schema-events-waits-history-long-size 100 +performance-schema-events-waits-history-size 5 +performance-schema-hosts-size 20 performance-schema-instrument performance-schema-max-cond-classes 80 -performance-schema-max-cond-instances 1000 +performance-schema-max-cond-instances 836 performance-schema-max-file-classes 50 performance-schema-max-file-handles 32768 -performance-schema-max-file-instances 10000 +performance-schema-max-file-instances 1556 performance-schema-max-mutex-classes 200 -performance-schema-max-mutex-instances 1000000 -performance-schema-max-rwlock-classes 30 -performance-schema-max-rwlock-instances 1000000 +performance-schema-max-mutex-instances 3282 +performance-schema-max-rwlock-classes 40 +performance-schema-max-rwlock-instances 1724 performance-schema-max-socket-classes 10 -performance-schema-max-socket-instances 1000 +performance-schema-max-socket-instances 179 performance-schema-max-stage-classes 150 -performance-schema-max-statement-classes 174 -performance-schema-max-table-handles 10000 -performance-schema-max-table-instances 1000 +performance-schema-max-statement-classes 175 +performance-schema-max-table-handles 445 +performance-schema-max-table-instances 445 performance-schema-max-thread-classes 50 -performance-schema-max-thread-instances 1000 +performance-schema-max-thread-instances 224 +performance-schema-session-connect-attrs-size 512 performance-schema-setup-actors-size 100 performance-schema-setup-objects-size 100 -performance-schema-users-size 100 +performance-schema-users-size 5 plugin-maturity unknown port 3306 port-open-timeout 0 diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index 33e10f29201..ffaa1a06b95 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2698,13 +2698,13 @@ DROP TABLE t1, t2; # DROP TABLE IF EXISTS `test1`; Warnings: -Note 1051 Unknown table 'test1' +Note 1051 Unknown table 'test.test1' CREATE TABLE `test1` ( `a1` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; DROP TABLE IF EXISTS `test2`; Warnings: -Note 1051 Unknown table 'test2' +Note 1051 Unknown table 'test.test2' CREATE TABLE `test2` ( `a2` int(11) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1; @@ -5241,7 +5241,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -5259,7 +5259,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' SET @@global.log_output= @old_log_output_state; SET @@global.slow_query_log= @old_slow_query_log_state; diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index c68d43831ad..940c3da9153 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -67,6 +67,19 @@ AND A.b = '06' AND A.c = 343; DROP TABLE t1; # +# Bug#59503: explain extended crash in get_mm_leaf +# +CREATE TABLE t1 (a VARCHAR(51) CHARACTER SET latin1) +ENGINE=MyISAM +PARTITION BY KEY (a) PARTITIONS 1; +INSERT INTO t1 VALUES ('a'),('b'),('c'); +EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE a > 1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where +Warnings: +Note 1003 select 1 AS `1` from `test`.`t1` where (`test`.`t1`.`a` > 1) +DROP TABLE t1; +# # Bug#57778: failed primary key add to partitioned innodb table # inconsistent and crashes # @@ -303,6 +316,32 @@ INSERT INTO t1 VALUES (NULL); SELECT * FROM t1 WHERE pk < 0 ORDER BY pk; pk DROP TABLE t1; +SET sql_mode=no_engine_substitution; +CREATE TABLE t1 (a INT) +ENGINE=NonExistentEngine; +ERROR 42000: Unknown storage engine 'NonExistentEngine' +CREATE TABLE t1 (a INT) +ENGINE=NonExistentEngine +PARTITION BY HASH (a); +ERROR 42000: Unknown storage engine 'NonExistentEngine' +CREATE TABLE t1 (a INT) +ENGINE=Memory; +ALTER TABLE t1 ENGINE=NonExistentEngine; +ERROR 42000: Unknown storage engine 'NonExistentEngine' +ALTER TABLE t1 +PARTITION BY HASH (a) +(PARTITION p0 ENGINE=Memory, +PARTITION p1 ENGINE=NonExistentEngine); +ERROR 42000: Unknown storage engine 'NonExistentEngine' +ALTER TABLE t1 ENGINE=NonExistentEngine; +ERROR 42000: Unknown storage engine 'NonExistentEngine' +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MEMORY DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET sql_mode=''; CREATE TABLE t1 (a INT) ENGINE=NonExistentEngine; Warnings: @@ -339,6 +378,7 @@ t1 CREATE TABLE `t1` ( (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) */ DROP TABLE t1; +SET sql_mode=DEFAULT; CREATE TABLE t1 (a INT NOT NULL, KEY(a)) PARTITION BY RANGE(a) (PARTITION p1 VALUES LESS THAN (200), PARTITION pmax VALUES LESS THAN MAXVALUE); @@ -1056,13 +1096,13 @@ select * from t1 where f1 = 10; f1 f2 10 1 drop table t1; -set session storage_engine= 'memory'; +set session default_storage_engine= 'memory'; create table t1 (f_int1 int(11) default null) engine = memory partition by range (f_int1) subpartition by hash (f_int1) (partition part1 values less than (1000) (subpartition subpart11 engine = memory)); drop table t1; -set session storage_engine='myisam'; +set session default_storage_engine='myisam'; create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) partition by hash(f_int1) partitions 2; insert into t1 values (1,1),(2,2); @@ -1764,13 +1804,13 @@ engine=MEMORY partition by key (a); REPAIR TABLE t1; Table Op Msg_type Msg_text -test.t1 repair status OK +test.t1 repair note The storage engine for the table doesn't support repair OPTIMIZE TABLE t1; Table Op Msg_type Msg_text test.t1 optimize note The storage engine for the table doesn't support optimize CHECK TABLE t1; Table Op Msg_type Msg_text -test.t1 check status OK +test.t1 check note The storage engine for the table doesn't support check ANALYZE TABLE t1; Table Op Msg_type Msg_text test.t1 analyze note The storage engine for the table doesn't support analyze @@ -1885,8 +1925,7 @@ WHERE t1.id IN ( SELECT distinct id FROM t4 WHERE taken BETWEEN @f_date AND date_add(@t_date, INTERVAL 1 DAY)) -ORDER BY t1.id -; +ORDER BY t1.id; MyISAM_part 16421 19092 @@ -1907,7 +1946,7 @@ INSERT INTO t1 VALUES ('2006-09-29 21:50:01',22589,'Verified'); DROP TABLE IF EXISTS t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t2 ( id int(8) NOT NULL, severity tinyint(4) NOT NULL DEFAULT '0', diff --git a/mysql-test/r/partition_binlog.result b/mysql-test/r/partition_binlog.result index 1db427868f4..21eca8f1c00 100644 --- a/mysql-test/r/partition_binlog.result +++ b/mysql-test/r/partition_binlog.result @@ -9,7 +9,7 @@ PARTITION BY RANGE (id) PARTITION pmax VALUES LESS THAN (MAXVALUE)); INSERT INTO t1 VALUES (1), (10), (100), (1000); ALTER TABLE t1 TRUNCATE PARTITION p1; -ERROR HY000: Incorrect partition name +ERROR HY000: Unknown partition 'p1' in table 't1' ALTER TABLE t1 DROP PARTITION p1; ERROR HY000: Error in list of partitions to DROP # No error returned, output in table format instead: diff --git a/mysql-test/r/partition_debug_sync.result b/mysql-test/r/partition_debug_sync.result index 0549a6a8bdd..c30651c1c0d 100644 --- a/mysql-test/r/partition_debug_sync.result +++ b/mysql-test/r/partition_debug_sync.result @@ -5,7 +5,9 @@ SET DEBUG_SYNC= 'RESET'; # Test when remove partitioning is done while drop table is waiting # for the table. # After MDL was introduced, there is no longer any race, so test is done -# by adding a small sleep to verify that the delete waits. +# by adding a small sleep to verify that the delete waits. This happens +# only until ALTER tries to upgrade its MDL lock, which ends up in MDL +# deadlock which is correctly reported. # Con 1 SET DEBUG_SYNC= 'RESET'; CREATE TABLE t1 @@ -19,14 +21,15 @@ PARTITION p1 VALUES LESS THAN (20), PARTITION p2 VALUES LESS THAN (100), PARTITION p3 VALUES LESS THAN MAXVALUE ) */; SET DEBUG_SYNC= 'alter_table_before_create_table_no_lock SIGNAL removing_partitioning WAIT_FOR waiting_for_alter'; -SET DEBUG_SYNC= 'alter_table_before_main_binlog SIGNAL partitioning_removed'; +SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL waiting_for_upgrade'; ALTER TABLE t1 REMOVE PARTITIONING; # Con default SET DEBUG_SYNC= 'now WAIT_FOR removing_partitioning'; SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL waiting_for_alter'; -SET DEBUG_SYNC= 'rm_table_no_locks_before_delete_table WAIT_FOR partitioning_removed'; +SET DEBUG_SYNC= 'rm_table_no_locks_before_delete_table WAIT_FOR waiting_for_upgrade'; DROP TABLE IF EXISTS t1; # Con 1 +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction SET DEBUG_SYNC= 'RESET'; SET DEBUG_SYNC= 'RESET'; # @@ -58,3 +61,27 @@ SET DEBUG_SYNC= 'RESET'; # Con default SET DEBUG_SYNC= 'RESET'; End of 5.1 tests +# +# Coverage test for non pruned ha_partition::store_lock() +# +CREATE TABLE t1 (a int) ENGINE = InnoDB; +CREATE TABLE t2 (a int PRIMARY KEY) +ENGINE = InnoDB PARTITION BY HASH (a) PARTITIONS 3; +HANDLER t1 OPEN; +# Con1 +LOCK TABLES t1 WRITE, t2 READ; +# Default +SET DEBUG_SYNC="wait_for_lock SIGNAL locking"; +INSERT INTO t2 VALUES (1), (2), (3); +# Con1 +SET DEBUG_SYNC="now WAIT_FOR locking"; +ALTER TABLE t1 ADD COLUMN b int; +# Default +ERROR HY000: Wait on a lock was aborted due to a pending exclusive lock +SELECT 1; +1 +1 +# Con1 +UNLOCK TABLES; +# Default +DROP TABLE t1, t2; diff --git a/mysql-test/r/partition_disabled.result b/mysql-test/r/partition_disabled.result index 505bec79610..edf3a56d9b2 100644 --- a/mysql-test/r/partition_disabled.result +++ b/mysql-test/r/partition_disabled.result @@ -56,7 +56,7 @@ ERROR HY000: The MariaDB server is running with the --skip-partition option so i ALTER TABLE t1 PARTITION BY KEY(joined) PARTITIONS 2; ERROR HY000: The MariaDB server is running with the --skip-partition option so it cannot execute this statement drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' CREATE TABLE t1 ( firstname VARCHAR(25) NOT NULL, lastname VARCHAR(25) NOT NULL, @@ -73,7 +73,7 @@ PARTITION p4 VALUES LESS THAN MAXVALUE ); ERROR HY000: The MariaDB server is running with the --skip-partition option so it cannot execute this statement drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' CREATE TABLE t1 (id INT, purchased DATE) PARTITION BY RANGE( YEAR(purchased) ) SUBPARTITION BY HASH( TO_DAYS(purchased) ) @@ -84,7 +84,7 @@ PARTITION p2 VALUES LESS THAN MAXVALUE ); ERROR HY000: The MariaDB server is running with the --skip-partition option so it cannot execute this statement drop table t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' create table t1 (a varchar(10) charset latin1 collate latin1_bin); insert into t1 values (''),(' '),('a'),('a '),('a '); explain partitions select * from t1 where a='a ' OR a='a'; diff --git a/mysql-test/r/partition_explicit_prune.result b/mysql-test/r/partition_explicit_prune.result new file mode 100644 index 00000000000..a7926554c33 --- /dev/null +++ b/mysql-test/r/partition_explicit_prune.result @@ -0,0 +1,1865 @@ +# +# Bug#13559657: PARTITION SELECTION DOES NOT WORK WITH VIEWS +# +CREATE TABLE t1 (a int) +ENGINE = InnoDB +PARTITION BY HASH (a) PARTITIONS 2; +INSERT INTO t1 VALUES (0), (1), (2), (3); +CREATE VIEW v1 AS SELECT a FROM t1 PARTITION (p0); +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` PARTITION (`p0`) latin1 latin1_swedish_ci +FLUSH STATUS; +SELECT * FROM v1; +a +0 +2 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 3 +HANDLER_TMP_WRITE 22 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +SELECT a FROM t1 PARTITION (p0); +a +0 +2 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 3 +HANDLER_TMP_WRITE 22 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +INSERT INTO v1 VALUES (10); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_TMP_WRITE 22 +# 2 locks (1 table, all partitions pruned) +FLUSH STATUS; +SELECT * FROM v1; +a +0 +10 +2 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 4 +HANDLER_TMP_WRITE 22 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +SELECT a FROM t1 PARTITION (p0); +a +0 +10 +2 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 4 +HANDLER_TMP_WRITE 22 +# 4 locks (1 table, 1 partition lock/unlock) +SELECT * FROM t1; +a +0 +1 +10 +2 +3 +DROP VIEW v1; +CREATE VIEW v1 AS SELECT a FROM t1 PARTITION (p0) WITH CHECK OPTION; +FLUSH STATUS; +INSERT INTO v1 VALUES (20); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_TMP_WRITE 22 +# 2 locks (1 table, all partitions pruned) +SELECT * FROM v1; +a +0 +10 +2 +20 +SELECT * FROM t1; +a +0 +1 +10 +2 +20 +3 +DROP VIEW v1; +CREATE VIEW v1 AS +SELECT a FROM t1 PARTITION (p0) WHERE a = 30 WITH CHECK OPTION; +FLUSH STATUS; +INSERT INTO v1 VALUES (30); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# 4 locks (1 table, 1 partition lock/unlock) +FLUSH STATUS; +INSERT INTO v1 VALUES (31); +ERROR HY000: CHECK OPTION failed 'test.v1' +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +# 2 locks (1 table, all partitions pruned) +FLUSH STATUS; +INSERT INTO v1 VALUES (32); +ERROR HY000: CHECK OPTION failed 'test.v1' +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +# 4 locks (1 table, 1 partition lock/unlock) +SELECT * FROM v1; +a +30 +SELECT * FROM t1; +a +0 +1 +10 +2 +20 +3 +30 +DROP VIEW v1; +DROP TABLE t1; +# Original tests for WL#5217 +# Must have InnoDB as engine to get the same statistics results. +# embedded uses MyISAM as default. CREATE SELECT uses the default engine. +SET @old_default_storage_engine = @@default_storage_engine; +SET @@default_storage_engine = 'InnoDB'; +# Test to show if I_S affects HANDLER_ counts +FLUSH STATUS; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_TMP_WRITE 22 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +# OK, seems to add number of variables processed before HANDLER_WRITE +# and number of variables + 1 evaluated in the previous call in RND_NEXT +CREATE TABLE t1 +(a INT NOT NULL, +b varchar (64), +INDEX (b,a), +PRIMARY KEY (a)) +ENGINE = InnoDB +PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) SUBPARTITIONS 2 +(PARTITION pNeg VALUES LESS THAN (0) +(SUBPARTITION subp0, +SUBPARTITION subp1), +PARTITION `p0-9` VALUES LESS THAN (10) +(SUBPARTITION subp2, +SUBPARTITION subp3), +PARTITION `p10-99` VALUES LESS THAN (100) +(SUBPARTITION subp4, +SUBPARTITION subp5), +PARTITION `p100-99999` VALUES LESS THAN (100000) +(SUBPARTITION subp6, +SUBPARTITION subp7)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` varchar(64) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`,`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) +(PARTITION pNeg VALUES LESS THAN (0) + (SUBPARTITION subp0 ENGINE = InnoDB, + SUBPARTITION subp1 ENGINE = InnoDB), + PARTITION `p0-9` VALUES LESS THAN (10) + (SUBPARTITION subp2 ENGINE = InnoDB, + SUBPARTITION subp3 ENGINE = InnoDB), + PARTITION `p10-99` VALUES LESS THAN (100) + (SUBPARTITION subp4 ENGINE = InnoDB, + SUBPARTITION subp5 ENGINE = InnoDB), + PARTITION `p100-99999` VALUES LESS THAN (100000) + (SUBPARTITION subp6 ENGINE = InnoDB, + SUBPARTITION subp7 ENGINE = InnoDB)) */ +# First test that the syntax is OK +SHOW CREATE TABLE t1 PARTITION (subp0); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PARTITION (subp0)' at line 1 +# Not a correct partition list +INSERT INTO t1 PARTITION () VALUES (1, "error"); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') VALUES (1, "error")' at line 1 +INSERT INTO t1 PARTITION (pNonExisting) VALUES (1, "error"); +ERROR HY000: Unknown partition 'pNonExisting' in table 't1' +INSERT INTO t1 PARTITION (pNeg, pNonExisting) VALUES (1, "error"); +ERROR HY000: Unknown partition 'pNonExisting' in table 't1' +# Duplicate partitions and overlapping partitions and subpartitios is OK +FLUSH STATUS; +INSERT INTO t1 PARTITION (pNeg, pNeg) VALUES (-1, "pNeg(-subp1)"); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# Should be 1 commit +# 4 external locks (due to pruning of locks) +# (1 ha_partition + 1 ha_innobase) x 2 (lock + unlock) +# and 18 write (1 ha_innobase + 17 internal I_S write) +INSERT INTO t1 PARTITION (pNeg, subp0) VALUES (-3, "pNeg(-subp1)"); +INSERT INTO t1 PARTITION (pNeg, subp0) VALUES (-2, "(pNeg-)subp0"); +# should be correct +INSERT INTO t1 PARTITION (`p100-99999`) VALUES (100, "`p100-99999`(-subp6)"), (101, "`p100-99999`(-subp7)"), (1000, "`p100-99999`(-subp6)"); +INSERT INTO t1 PARTITION(`p10-99`,subp3) VALUES (1, "subp3"), (10, "p10-99"); +FLUSH STATUS; +INSERT INTO t1 PARTITION(subp3) VALUES (3, "subp3"); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# Should be 1 commit +# 4 external locks +# (1 ha_partition + 1 ha_innobase) x 2 (lock + unlock) +# and 18 write (1 ha_innobase + 17 internal I_S write) +FLUSH STATUS; +LOCK TABLE t1 WRITE; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# should be 1 commit +# 9 locks (1 ha_partition + 8 ha_innobase) +# 17 writes (internal I_S) +INSERT INTO t1 PARTITION(`p0-9`) VALUES (5, "p0-9:subp3"); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +HANDLER_WRITE 2 +# + 1 commit +# + 19 rnd next (internal I_S) +# + 19 write (18 internal I_S + 1 insert) +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_RND_NEXT 52 +HANDLER_TMP_WRITE 72 +HANDLER_WRITE 2 +# + 9 locks (unlocks) +# + 19 rnd next (internal I_S) +# + 18 write (internal I_S) +# Not matching partitions with inserted value +INSERT INTO t1 PARTITION (pNeg, pNeg) VALUES (1, "error"); +ERROR HY000: Found a row not matching the given partition set +INSERT INTO t1 PARTITION (pNeg, subp0) VALUES (1, "error"); +ERROR HY000: Found a row not matching the given partition set +INSERT INTO t1 PARTITION (`p100-99999`) VALUES (1, "error"), (10, "error"); +ERROR HY000: Found a row not matching the given partition set +INSERT INTO t1 VALUES (1000000, "error"), (9999999, "error"); +ERROR HY000: Table has no partition for value 1000000 +INSERT INTO t1 PARTITION (`p100-99999`) VALUES (1000000, "error"), (9999999, "error"); +ERROR HY000: Table has no partition for value 1000000 +INSERT INTO t1 PARTITION (pNeg, subp4) VALUES (-7, "pNeg(-subp1)"), (-10, "pNeg(-subp0)"), (-1, "pNeg(-subp1)"), (-99, "pNeg(-subp1)"); +Got one of the listed errors +SELECT * FROM t1 ORDER BY a; +a b +-3 pNeg(-subp1) +-2 (pNeg-)subp0 +-1 pNeg(-subp1) +1 subp3 +3 subp3 +5 p0-9:subp3 +10 p10-99 +100 `p100-99999`(-subp6) +101 `p100-99999`(-subp7) +1000 `p100-99999`(-subp6) +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +SET @save_innodb_stats_on_metadata=@@global.innodb_stats_on_metadata; +SET @@global.innodb_stats_on_metadata=ON; +SELECT PARTITION_NAME, SUBPARTITION_NAME, TABLE_ROWS +FROM INFORMATION_SCHEMA.PARTITIONS +WHERE TABLE_SCHEMA = 'test' +AND TABLE_NAME = 't1' ORDER BY SUBPARTITION_NAME; +PARTITION_NAME SUBPARTITION_NAME TABLE_ROWS +pNeg subp0 1 +pNeg subp1 2 +p0-9 subp2 0 +p0-9 subp3 3 +p10-99 subp4 1 +p10-99 subp5 0 +p100-99999 subp6 2 +p100-99999 subp7 1 +SET @@global.innodb_stats_on_metadata=@save_innodb_stats_on_metadata; +FLUSH STATUS; +SELECT * FROM t1 PARTITION (pNonexistent); +ERROR HY000: Unknown partition 'pNonexistent' in table 't1' +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_TMP_WRITE 22 +# should have failed before locking (only 17 internal I_S writes) +FLUSH STATUS; +SELECT * FROM t1 PARTITION (subp2); +a b +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 1 +HANDLER_TMP_WRITE 22 +# Should be 1 commit +# 4 locks (1 ha_partition + 1 ha_innobase) x 2 (lock/unlock) +# 1 read first (also calls index_read) +# 2 read key (first from innobase_get_index and second from index first) +# 17 writes (internal I_S) +FLUSH STATUS; +SELECT * FROM t1 PARTITION (subp2,pNeg) AS TableAlias; +a b +-2 (pNeg-)subp0 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 3 +HANDLER_READ_NEXT 3 +HANDLER_TMP_WRITE 22 +# Should be 1 commit +# 8 locks (1 ha_partition + 2 + 1 ha_innobase) x 2 +# 3 read first (one for each partition) +# 6 read key (3 from read first and 3 from innobase_get_index) +# 3 read next (one next call after each read row) +# 17 writes (internal I_S) +FLUSH STATUS; +LOCK TABLE t1 READ, t1 as TableAlias READ; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 1 commit +# 18 locks +# 18 READ KEY from opening a new partition table instance, +# (1 innobase_get_index for each index, per partition, 1 x 2 x 8 = 16 +# + info(HA_STATUS_CONST) call on the partition with the most number +# of rows, 2 innobase_get_index for updating both index statistics) +# 17 writes (internal I_S) +SELECT * FROM t1 PARTITION (subp3) AS TableAlias; +a b +5 p0-9:subp3 +1 subp3 +3 subp3 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_FIRST 1 +HANDLER_READ_NEXT 3 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +# + 1 commit +# + 1 read first (read first key from index in one partition) +# + 2 read key (innobase_get_index from index_init + from index_first) +# + 3 read next (one after each row) +# + 19 rnd next (from the last I_S query) +# + 18 write (internal I_S) +SELECT COUNT(*) FROM t1 PARTITION (`p10-99`); +COUNT(*) +1 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_READ_FIRST 3 +HANDLER_READ_NEXT 4 +HANDLER_READ_RND_NEXT 52 +HANDLER_TMP_WRITE 72 +# + 1 commit +# + 2 read first (one for each subpart) +# + 4 read key (innobase_get_index from index_init + from index_first) +# + 1 read next (one after each row) +# + 19 rnd next (from the last I_S query) +# + 18 write (internal I_S) +SELECT * FROM t1 WHERE a = 1000000; +a b +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 4 +HANDLER_READ_FIRST 3 +HANDLER_READ_NEXT 4 +HANDLER_READ_RND_NEXT 78 +HANDLER_TMP_WRITE 97 +# No matching partition, only internal I_S. +SELECT * FROM t1 PARTITION (pNeg) WHERE a = 100; +a b +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 5 +HANDLER_READ_FIRST 3 +HANDLER_READ_NEXT 4 +HANDLER_READ_RND_NEXT 104 +HANDLER_TMP_WRITE 122 +# + 18 for unlock (same as lock above) (100 is not in pNeg, no match) +# Test that EXPLAIN PARTITION works +EXPLAIN PARTITIONS SELECT * FROM t1 PARTITION (pNonexistent); +ERROR HY000: Unknown partition 'pNonexistent' in table 't1' +EXPLAIN PARTITIONS SELECT * FROM t1 PARTITION (subp2); +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p0-9_subp2 index NULL b 71 NULL 2 Using index +FLUSH STATUS; +EXPLAIN PARTITIONS SELECT * FROM t1 PARTITION (subp2,pNeg) AS TableAlias; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE TableAlias pNeg_subp0,pNeg_subp1,p0-9_subp2 index NULL b 71 NULL 4 Using index +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 8 locks (1 ha_partition + 3 ha_innobase) x 2 (lock/unlock) +EXPLAIN PARTITIONS SELECT * FROM t1 PARTITION (subp3) AS TableAlias; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE TableAlias p0-9_subp3 index NULL b 71 NULL 3 Using index +EXPLAIN PARTITIONS SELECT COUNT(*) FROM t1 PARTITION (`p10-99`); +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p10-99_subp4,p10-99_subp5 index NULL PRIMARY 4 NULL 2 Using index +EXPLAIN PARTITIONS SELECT * FROM t1 WHERE a = 1000000; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +EXPLAIN PARTITIONS SELECT * FROM t1 PARTITION (pNeg) WHERE a = 100; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +# Test how it changes the alias/keywords/reserved words +SELECT * FROM t1 PARTITION; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1 +SELECT * FROM t1 `PARTITION`; +a b +-2 (pNeg-)subp0 +5 p0-9:subp3 +10 p10-99 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +1 subp3 +3 subp3 +100 `p100-99999`(-subp6) +1000 `p100-99999`(-subp6) +101 `p100-99999`(-subp7) +SELECT * FROM t1 AS PARTITION; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PARTITION' at line 1 +SELECT * FROM t1 AS `PARTITION`; +a b +-2 (pNeg-)subp0 +5 p0-9:subp3 +10 p10-99 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +1 subp3 +3 subp3 +100 `p100-99999`(-subp6) +1000 `p100-99999`(-subp6) +101 `p100-99999`(-subp7) +# +# Test REPLACE +# +FLUSH STATUS; +REPLACE INTO t1 PARTITION (subp0) VALUES (-21, 'Should fail!'); +ERROR HY000: Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 1 +# 2 locks (1 ha_partition) x 2 (lock/unlock), Was 4 locks before WL4443 +# explicit pruning says part_id 0 and implicit pruning says part_id 1 +# so no partition will be locked! +# 0 rollback (since no locked partition) +# 17 writes (I_S internal) +FLUSH STATUS; +REPLACE INTO t1 PARTITION (subp1) VALUES (-21, 'Insert by REPLACE'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 2 +# 1 commit +# 4 locks (1 ha_partition + 1 ha_innobase) x 2 (lock/unlock) +# 18 writes (17 I_S internal, 1 ha_innobase) +SELECT * FROM t1 PARTITION (pNeg); +a b +-2 (pNeg-)subp0 +-21 Insert by REPLACE +-3 pNeg(-subp1) +-1 pNeg(-subp1) +FLUSH STATUS; +REPLACE INTO t1 PARTITION (subp1) VALUES (-21, 'REPLACEd by REPLACE'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_KEY 2 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 2 +HANDLER_WRITE 2 +# 1 commit +# 4 locks (1 ha_partition + 1 ha_innobase) x 2 (lock/unlock) +# 2 read key (1 innobase_get_index when init the index + 1 index read +# to get the position to update) +# 1 update (updated one row, since there is no delete trigger, update +# is used instead of delete+insert) +# 18 write (17 from I_S, 1 for the failed insert) +SELECT * FROM t1 PARTITION (pNeg); +a b +-2 (pNeg-)subp0 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +-21 REPLACEd by REPLACE +FLUSH STATUS; +LOCK TABLE t1 WRITE; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 1 commit +# 9 locks +# 17 write (internal I_S) +DELETE FROM t1 PARTITION(subp1) WHERE b = "REPLACEd by REPLACE"; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_DELETE 2 +HANDLER_READ_KEY 1 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +# + 1 commit +# + 1 delete (one row deleted) +# + 3 read key (1 innodb_get_index in records_in_range, +# 1 innodb_get_index in index_init, 1 index_read in index_read_first) +# + 1 read next (search for another row in secondary index) +# + 19 rnd next (internal I_S) +# + 18 write (internal I_S) +REPLACE INTO t1 PARTITION (subp0) VALUES (-21, 'Should fail!'); +ERROR HY000: Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_DELETE 2 +HANDLER_READ_KEY 1 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 52 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 72 +HANDLER_WRITE 1 +# Failed before start_stmt/execution. +# + 19 rnd next (internal I_S) +# 0 rollback (No partition had called start_stmt, all parts pruned) +# + 18 write (internal I_S) +REPLACE INTO t1 PARTITION (pNeg) VALUES (-21, 'Insert by REPLACE'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_DELETE 2 +HANDLER_READ_KEY 1 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 78 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 97 +HANDLER_WRITE 3 +# + 1 commit +# + 19 rnd next (internal I_S) +# + 19 write (18 internal I_S + 1 real write) +REPLACE INTO t1 PARTITION (subp1) VALUES (-21, 'REPLACEd by REPLACE'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 4 +HANDLER_DELETE 2 +HANDLER_READ_KEY 3 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 104 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 122 +HANDLER_UPDATE 2 +HANDLER_WRITE 5 +# + 1 commit +# + 2 read key (see non locked query) +# + 19 rnd next (internal I_S) +# + 1 update (see non locked query) +# + 19 write (18 internal I_S + 1 failed write) +SELECT * FROM t1 PARTITION (subp1); +a b +-3 pNeg(-subp1) +-1 pNeg(-subp1) +-21 REPLACEd by REPLACE +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 5 +HANDLER_DELETE 2 +HANDLER_READ_FIRST 1 +HANDLER_READ_KEY 3 +HANDLER_READ_NEXT 4 +HANDLER_READ_RND_NEXT 130 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 147 +HANDLER_UPDATE 2 +HANDLER_WRITE 5 +# + 1 commit +# + 1 read first +# + 2 read key +# + 3 read next +# + 19 rnd next (internal I_S) +# + 18 write (internal I_S) +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 5 +HANDLER_DELETE 2 +HANDLER_READ_FIRST 1 +HANDLER_READ_KEY 3 +HANDLER_READ_NEXT 4 +HANDLER_READ_RND_NEXT 156 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 172 +HANDLER_UPDATE 2 +HANDLER_WRITE 5 +# + 9 locks +# + 19 rnd next (internal I_S) +# + 18 write (internal I_S) +# +# Test LOAD +# +SELECT * FROM t1 PARTITION (pNeg, `p10-99`); +a b +-2 (pNeg-)subp0 +10 p10-99 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +-21 REPLACEd by REPLACE +FLUSH STATUS; +SELECT * FROM t1 PARTITION (pNeg, `p10-99`) INTO OUTFILE 'loadtest.txt'; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 4 +HANDLER_READ_NEXT 5 +HANDLER_TMP_WRITE 22 +# 1 commit +# 10 locks (1 ha_partition + 4 ha_innobase) x 2 (lock/unlock) +# 4 read first (for reading the first row in 4 partitions) +# 8 read key (4 from read first + 4 for index init) +# 5 read next (one after each row) +# 17 write (internal I_S) +FLUSH STATUS; +ALTER TABLE t1 TRUNCATE PARTITION pNeg, `p10-99`; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 10 locks (table + 4 partition) x (lock + unlock) +SELECT * FROM t1 PARTITION (pNeg, `p10-99`); +a b +FLUSH STATUS; +LOAD DATA INFILE 'loadtest.txt' INTO TABLE t1 PARTITION (pNeg); +ERROR HY000: Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 3 +# 6 locks (1 ha_partition + 2 ha_innobase) x 2 (lock+unlock) +# 1 rollback +SELECT * FROM t1 PARTITION (pNeg, `p10-99`); +a b +FLUSH STATUS; +LOAD DATA INFILE 'loadtest.txt' INTO TABLE t1 PARTITION (pNeg, subp4, subp5); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 10 +# 10 lock (1 ha_partition + 4 ha_innobase) x 2 (lock + unlock) +ALTER TABLE t1 TRUNCATE PARTITION pNeg, `p10-99`; +FLUSH STATUS; +LOCK TABLE t1 WRITE; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 9 locks +# 18 read key (ALTER forces table to be closed, see above for open) +LOAD DATA INFILE 'loadtest.txt' INTO TABLE t1 PARTITION (pNeg, `p10-99`); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +HANDLER_WRITE 10 +# + 23 write (18 internal I_S + 5 rows) +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_RND_NEXT 52 +HANDLER_TMP_WRITE 72 +HANDLER_WRITE 10 +# + 9 locks +# +# Test UPDATE +# +FLUSH STATUS; +UPDATE t1 PARTITION(subp0) SET b = concat(b, ', Updated'); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 2 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 2 +# 1 commit +# 4 lock (1 ha_partition + 1 ha_innobase) x 2 (lock + unlock) +# 1 read first (read first row, called from first rnd_next) +# 2 read key (innobase_get_index from rnd_init + +# read next row from second rnd_next) +# 1 update (update the row) +SELECT * FROM t1 PARTITION (subp0) ORDER BY a; +a b +-2 (pNeg-)subp0, Updated +FLUSH STATUS; +UPDATE t1 PARTITION(subp0) SET b = concat(b, ', Updated2') WHERE a = -2; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_KEY 1 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 2 +# 1 commit +# 4 lock +# 1 read key +# 1 update +FLUSH STATUS; +UPDATE t1 PARTITION(subp0) SET a = -4, b = concat(b, ', Updated from a = -2') +WHERE a = -2; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_KEY 1 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 2 +# 1 commit +# 4 lock +# 2 read key - (2 index read) +# 1 read rnd - rnd_pos +# 1 update +FLUSH STATUS; +UPDATE t1 PARTITION(subp0) SET b = concat(b, ', Updated2') WHERE a = 100; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# Nothing, since impossible PARTITION+WHERE clause. +FLUSH STATUS; +UPDATE t1 PARTITION(subp0) SET a = -2, b = concat(b, ', Updated from a = 100') +WHERE a = 100; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# Nothing, since impossible PARTITION+WHERE clause. +FLUSH STATUS; +UPDATE t1 PARTITION(`p100-99999`) SET a = -2, b = concat(b, ', Updated from a = 100') +WHERE a = 100; +ERROR HY000: Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_READ_KEY 1 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 1 +# 6 lock +# 4 read key (1 index init + 1 index read + 1 rnd init + 1 rnd pos) +# 1 read rnd (rnd pos) +# 1 rollback +FLUSH STATUS; +UPDATE t1 PARTITION(`p100-99999`, pNeg) SET a = -4, b = concat(b, ', Updated from a = 100') +WHERE a = 100; +Got one of the listed errors +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_READ_KEY 1 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 1 +HANDLER_WRITE 1 +# 10 locks +# 4 read key +# 1 read rnd +# 1 rollback +# 18 write (17 internal I_S + 1 failed insert) +FLUSH STATUS; +UPDATE t1 PARTITION(`p100-99999`, pNeg) SET a = -222, b = concat(b, ', Updated from a = 100') +WHERE a = 100; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_DELETE 1 +HANDLER_READ_KEY 1 +HANDLER_TMP_WRITE 22 +HANDLER_UPDATE 1 +HANDLER_WRITE 1 +# 1 commit +# 1 delete +# 4 read key +# 1 read rnd +# 18 write (17 internal I_S + 1 insert) +SELECT * FROM t1 ORDER BY a; +a b +-222 `p100-99999`(-subp6), Updated from a = 100 +-21 REPLACEd by REPLACE +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +1 subp3 +3 subp3 +5 p0-9:subp3 +10 p10-99 +101 `p100-99999`(-subp7) +1000 `p100-99999`(-subp6) +# Test of non matching partition (i.e ER_NO_PARTITION_FOUND) +FLUSH STATUS; +UPDATE t1 SET b = concat(b, ', Updated2') WHERE a = 1000000; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# Nothing (no matching partition found) +FLUSH STATUS; +UPDATE t1 PARTITION (pNeg) SET b = concat(b, ', Updated2') WHERE a = 1000000; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# Nothing (no matching partition found) +FLUSH STATUS; +LOCK TABLE t1 WRITE; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 9 locks +UPDATE t1 PARTITION (subp7) SET b = concat(b, ', Updated to 103'), a = 103 WHERE a = 101; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_KEY 1 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +HANDLER_UPDATE 2 +# + 4 read key +# + 1 read rnd +# + 1 update +UPDATE t1 PARTITION (`p100-99999`) SET b = concat(b, ', Updated to 110'), a = 110 WHERE a = 103; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_DELETE 1 +HANDLER_READ_KEY 2 +HANDLER_READ_RND_NEXT 52 +HANDLER_TMP_WRITE 72 +HANDLER_UPDATE 3 +HANDLER_WRITE 1 +# + 1 delete +# + 4 read key +# + 1 read rnd +# + 19 write (18 internal I_S + 1 insert) +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_DELETE 1 +HANDLER_READ_KEY 2 +HANDLER_READ_RND_NEXT 78 +HANDLER_TMP_WRITE 97 +HANDLER_UPDATE 3 +HANDLER_WRITE 1 ++ 9 locks +# +# Test DELETE +# +SELECT * FROM t1 ORDER BY b, a; +a b +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +5 p0-9:subp3 +10 p10-99 +-3 pNeg(-subp1) +-1 pNeg(-subp1) +-21 REPLACEd by REPLACE +1 subp3 +3 subp3 +1000 `p100-99999`(-subp6) +-222 `p100-99999`(-subp6), Updated from a = 100 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +FLUSH STATUS; +DELETE FROM t1 PARTITION (pNeg) WHERE a = -1; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_DELETE 2 +HANDLER_READ_KEY 1 +HANDLER_TMP_WRITE 22 +# 1 delete +# 4 locks (pruning works!). +# 1 read key (index read) +FLUSH STATUS; +DELETE FROM t1 PARTITION (subp1) WHERE b like '%subp1%'; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_DELETE 2 +HANDLER_READ_RND_NEXT 3 +HANDLER_TMP_WRITE 22 +# 1 delete +# 4 locks +# 1 read first +# 2 read key +# 3 read rnd +FLUSH STATUS; +LOCK TABLE t1 WRITE; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 9 locks +DELETE FROM t1 PARTITION (subp1) WHERE b = 'p0-9:subp3'; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 2 +HANDLER_READ_KEY 1 +HANDLER_READ_RND_NEXT 26 +HANDLER_TMP_WRITE 47 +# + 3 read key (1 innodb_get_index in records_in_range +# + 1 innobase_get_index in index_init + 1 index read) +DELETE FROM t1 PARTITION (`p0-9`) WHERE b = 'p0-9:subp3'; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_DELETE 2 +HANDLER_READ_KEY 3 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 52 +HANDLER_TMP_WRITE 72 +# + 1 delete +# + 6 read key (same as above, but for two subpartitions) +# + 1 read next (read next after found row) +UNLOCK TABLES; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 3 +HANDLER_DELETE 2 +HANDLER_READ_KEY 3 +HANDLER_READ_NEXT 1 +HANDLER_READ_RND_NEXT 78 +HANDLER_TMP_WRITE 97 +# + 9 locks +# Test multi-table DELETE +# Can be expressed in two different ways. +CREATE TABLE t2 LIKE t1; +FLUSH STATUS; +INSERT INTO t2 PARTITION (`p10-99`, subp3, `p100-99999`) SELECT * FROM t1 PARTITION (subp3, `p10-99`, `p100-99999`); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 5 +HANDLER_READ_NEXT 5 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 10 +# 24 locks (2 table, 5 + 5 subpartitions lock/unlock) +FLUSH STATUS; +ALTER TABLE t2 TRUNCATE PARTITION `p10-99`, `p0-9`, `p100-99999`; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 14 locks (1 table, 6 subpartitions lock/unlock) +FLUSH STATUS; +INSERT INTO t2 PARTITION (subp3) SELECT * FROM t1 PARTITION (subp3, `p10-99`, `p100-99999`); +ERROR HY000: Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_READ_FIRST 5 +HANDLER_ROLLBACK 1 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 1 +# 16 locks (2 tables, 1 + 5 subpartitions lock/unlock) +FLUSH STATUS; +INSERT IGNORE INTO t2 PARTITION (subp3) SELECT * FROM t1 PARTITION (subp3, `p10-99`, `p100-99999`); +Warnings: +Warning 1748 Found a row not matching the given partition set +Warning 1748 Found a row not matching the given partition set +Warning 1748 Found a row not matching the given partition set +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 5 +HANDLER_READ_NEXT 5 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 7 +# 16 locks (2 tables, 1 + 5 subpartitions lock/unlock) +TRUNCATE TABLE t2; +FLUSH STATUS; +INSERT INTO t2 SELECT * FROM t1 PARTITION (subp3, `p10-99`, `p100-99999`); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 5 +HANDLER_READ_NEXT 5 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 10 +# 30 locks (2 table, 8 + 5 subpartitions lock/unlock) +FLUSH STATUS; +CREATE TABLE t3 SELECT * FROM t1 PARTITION (pNeg,subp3,`p100-99999`); +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_FIRST 5 +HANDLER_READ_NEXT 7 +HANDLER_TMP_WRITE 22 +HANDLER_WRITE 7 +# 14 locks (2 table, 5 subpartitions lock/unlock) +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` varchar(64) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`,`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) +(PARTITION pNeg VALUES LESS THAN (0) + (SUBPARTITION subp0 ENGINE = InnoDB, + SUBPARTITION subp1 ENGINE = InnoDB), + PARTITION `p0-9` VALUES LESS THAN (10) + (SUBPARTITION subp2 ENGINE = InnoDB, + SUBPARTITION subp3 ENGINE = InnoDB), + PARTITION `p10-99` VALUES LESS THAN (100) + (SUBPARTITION subp4 ENGINE = InnoDB, + SUBPARTITION subp5 ENGINE = InnoDB), + PARTITION `p100-99999` VALUES LESS THAN (100000) + (SUBPARTITION subp6 ENGINE = InnoDB, + SUBPARTITION subp7 ENGINE = InnoDB)) */ +SELECT * FROM t1; +a b +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +10 p10-99 +-21 REPLACEd by REPLACE +1 subp3 +3 subp3 +1000 `p100-99999`(-subp6) +-222 `p100-99999`(-subp6), Updated from a = 100 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` int(11) NOT NULL, + `b` varchar(64) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`,`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) +(PARTITION pNeg VALUES LESS THAN (0) + (SUBPARTITION subp0 ENGINE = InnoDB, + SUBPARTITION subp1 ENGINE = InnoDB), + PARTITION `p0-9` VALUES LESS THAN (10) + (SUBPARTITION subp2 ENGINE = InnoDB, + SUBPARTITION subp3 ENGINE = InnoDB), + PARTITION `p10-99` VALUES LESS THAN (100) + (SUBPARTITION subp4 ENGINE = InnoDB, + SUBPARTITION subp5 ENGINE = InnoDB), + PARTITION `p100-99999` VALUES LESS THAN (100000) + (SUBPARTITION subp6 ENGINE = InnoDB, + SUBPARTITION subp7 ENGINE = InnoDB)) */ +SELECT * FROM t2; +a b +10 p10-99 +1 subp3 +3 subp3 +1000 `p100-99999`(-subp6) +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` int(11) NOT NULL, + `b` varchar(64) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SELECT * FROM t3; +a b +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +-21 REPLACEd by REPLACE +1 subp3 +3 subp3 +1000 `p100-99999`(-subp6) +-222 `p100-99999`(-subp6), Updated from a = 100 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +FLUSH STATUS; +DELETE t1 PARTITION (pNeg), t3 FROM t1, t3 +WHERE t1.a = t3.a AND t3.b = 'subp3'; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PARTITION (pNeg), t3 FROM t1, t3 +WHERE t1.a = t3.a AND t3.b = 'subp3'' at line 1 +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_TMP_WRITE 22 +# Multi table delete without any matching rows +FLUSH STATUS; +DELETE t1, t2 FROM t1 PARTITION (pNeg), t3, t2 PARTITION (subp3) +WHERE t1.a = t3.a AND t3.b = 'subp3' AND t3.a = t2.a; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_READ_RND_NEXT 3 +HANDLER_TMP_WRITE 22 +# 12 locks (3 in t1, 1 in t3, 2 in t2) x 2 (lock + unlock) +# 1 read first (first rnd_next in t2) +# 4 read key (1 innodb_get_index in rnd_init in t2 + index read in t2 +# + 2 innodb_get_index in index_init in t1) +# 3 read rnd next (3 rnd next in t2, 2 rows + 1 empty) +# Multi table delete matching all rows in subp3 (2 rows in per table) +FLUSH STATUS; +DELETE FROM t2, t3 USING t2 PARTITION (`p0-9`), t3, t1 PARTITION (subp3) +WHERE t1.a = t3.a AND t3.b = 'subp3' AND t2.a = t1.a; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_DELETE 6 +HANDLER_READ_FIRST 1 +HANDLER_READ_KEY 2 +HANDLER_READ_NEXT 2 +HANDLER_READ_RND 2 +HANDLER_READ_RND_NEXT 16 +HANDLER_TMP_WRITE 22 +# 4 delete (2 in t2 + 2 in t3) +# 12 locks (3 in t2, 1 in t3, 2 in t1) x 2 (lock + unlock) +# 3 read first (1 in t1 + 1 in t3 + 1 in t3, for second row in t1) +# 17 read key (1 index_init in t1 + 1 read first in t1 + +# 2 index_init in t2 + 1 index read in t2 + +# 1 index_init in t3 + 1 index read in t3 + +# 1 index read in t2 + +# 1 index_init in t3 + 1 index read in t3 + +# 2 index_init in t2 + 2 index read in t2 (from rnd_pos) +# 1 index_init in t3 + 2 index read in t3 (from rnd_pos)) +# 2 read next (1 in t1 + 1 in t1, second row) +# 4 read rnd (position on 4 found rows to delete) +# 16 rnd next (8 in t3 + 8 in t3, for second row) +SELECT * FROM t1 ORDER BY a; +a b +-222 `p100-99999`(-subp6), Updated from a = 100 +-21 REPLACEd by REPLACE +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +1 subp3 +3 subp3 +10 p10-99 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +1000 `p100-99999`(-subp6) +SELECT * FROM t2 ORDER BY a; +a b +10 p10-99 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +1000 `p100-99999`(-subp6) +SELECT * FROM t3 ORDER BY a; +a b +-222 `p100-99999`(-subp6), Updated from a = 100 +-21 REPLACEd by REPLACE +-4 (pNeg-)subp0, Updated, Updated2, Updated from a = -2 +110 `p100-99999`(-subp7), Updated to 103, Updated to 110 +1000 `p100-99999`(-subp6) +# Test TRUNCATE TABLE (should fail, since one should use +# ALTER TABLE ... TRUNCATE PARTITION instead) +TRUNCATE TABLE t1 PARTITION(`p10-99`); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PARTITION(`p10-99`)' at line 1 +# Test of locking in TRUNCATE PARTITION +# Note that it does not support truncating subpartitions +FLUSH STATUS; +ALTER TABLE t1 TRUNCATE PARTITION pNeg; +SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS +WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0; +VARIABLE_NAME VARIABLE_VALUE +HANDLER_COMMIT 1 +HANDLER_TMP_WRITE 22 +# 6 locks (lock/unlock two subpartitions + table) +# Test on non partitioned table +SELECT * FROM t3 PARTITION (pNeg); +ERROR HY000: PARTITION () clause on non partitioned table +DROP TABLE t1, t2, t3; +# Test from superseeded WL# 2682 +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +CREATE TABLE `t1` ( +`id` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +PARTITION BY RANGE (id) ( +PARTITION p0 VALUES LESS THAN (6) ENGINE = MyISAM, +PARTITION p1 VALUES LESS THAN (11) ENGINE = MyISAM, +PARTITION p2 VALUES LESS THAN (16) ENGINE = MyISAM, +PARTITION p3 VALUES LESS THAN (21) ENGINE = MyISAM); +INSERT INTO `t1` VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), +(11), (12), (13), (14), (15), (16), (17), (18), (19), (20); +SELECT * FROM t1; +id +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +SELECT * FROM t1 PARTITION (p0); +id +1 +2 +3 +4 +5 +SELECT * FROM t1 PARTITION (p1); +id +6 +7 +8 +9 +10 +SELECT * FROM t1 PARTITION (p2); +id +11 +12 +13 +14 +15 +SELECT * FROM t1 PARTITION (p3); +id +16 +17 +18 +19 +20 +SELECT * FROM t1 PARTITION (p3) WHERE id = 2; +id +SELECT * FROM t1 PARTITION (foo); +ERROR HY000: Unknown partition 'foo' in table 't1' +CREATE TABLE `t2` ( +`id` int(11) NOT NULL DEFAULT 0, +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +PARTITION BY RANGE (id) ( +PARTITION p0 VALUES LESS THAN (6) ENGINE = MyISAM, +PARTITION p1 VALUES LESS THAN (11) ENGINE = MyISAM, +PARTITION p2 VALUES LESS THAN (16) ENGINE = MyISAM, +PARTITION p3 VALUES LESS THAN (21) ENGINE = MyISAM); +INSERT INTO `t2` VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), +(11), (12), (13), (14), (15), (16), (17), (18), (19), (20); +SELECT * FROM t2; +id +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +SELECT * FROM t2 PARTITION (p0); +id +1 +2 +3 +4 +5 +SELECT * FROM t2 PARTITION (p1); +id +6 +7 +8 +9 +10 +SELECT * FROM t2 PARTITION (p2); +id +11 +12 +13 +14 +15 +SELECT * FROM t2 PARTITION (p3); +id +16 +17 +18 +19 +20 +SELECT * FROM t2 PARTITION (p3) ORDER BY id; +id +16 +17 +18 +19 +20 +SELECT * FROM t2 PARTITION (p3) WHERE id = 2; +id +SELECT * FROM t2 PARTITION (foo); +ERROR HY000: Unknown partition 'foo' in table 't2' +CREATE TABLE `t3` ( +`id` int(32) default NULL, +`name` varchar(32) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +PARTITION BY LIST (id) ( +PARTITION p0 VALUES IN (1,3,5,7), +PARTITION p1 VALUES IN (0,2,4,6,8), +PARTITION p2 VALUES IN (9,10,11,12,13) +); +INSERT INTO `t3` VALUES (1,'first'), (3,'third'),(5,'fifth'),(7,'seventh'),(0,'zilch'),(2,'second'),(4,'fourth'),(6,'sixth'),(8,'eighth'),(9,'ninth'),(10,'tenth'),(11,'eleventh'),(12,'twelfth'),(13,'thirteenth'); +SELECT * FROM `t3`; +id name +1 first +3 third +5 fifth +7 seventh +0 zilch +2 second +4 fourth +6 sixth +8 eighth +9 ninth +10 tenth +11 eleventh +12 twelfth +13 thirteenth +SELECT * FROM `t3` PARTITION (p0); +id name +1 first +3 third +5 fifth +7 seventh +SELECT * FROM `t3` PARTITION (p1); +id name +0 zilch +2 second +4 fourth +6 sixth +8 eighth +SELECT * FROM `t3` PARTITION (p2); +id name +9 ninth +10 tenth +11 eleventh +12 twelfth +13 thirteenth +SELECT * FROM `t3` PARTITION (p2) ORDER BY id; +id name +9 ninth +10 tenth +11 eleventh +12 twelfth +13 thirteenth +DROP TABLE IF EXISTS `t4`; +Warnings: +Note 1051 Unknown table 'test.t4' +CREATE TABLE `t4` ( +`id` int(32) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (id) ; +INSERT INTO `t4` SELECT * FROM `t2`; +INSERT INTO `t4` SELECT * FROM `t2` ORDER BY id; +CREATE TABLE `t5` ( +id int(32), +name varchar(64), +purchased date) +PARTITION BY RANGE( YEAR(purchased) ) +SUBPARTITION BY HASH( TO_DAYS(purchased) ) ( +PARTITION p0 VALUES LESS THAN (1990) ( +SUBPARTITION s0, +SUBPARTITION s1 +), +PARTITION p1 VALUES LESS THAN (2000) ( +SUBPARTITION s2, +SUBPARTITION s3 +), +PARTITION p2 VALUES LESS THAN MAXVALUE ( +SUBPARTITION s4, +SUBPARTITION s5 +) +); +INSERT INTO `t5` VALUES (1, 'aaaaaaa', '2006-01-05 00:00:00'); +INSERT INTO `t5` VALUES (2, 'bbbbbbb', '2005-08-05 00:00:00'); +INSERT INTO `t5` VALUES (3, 'ccccccc', '1985-08-07 00:00:00'); +INSERT INTO `t5` VALUES (4, 'ddddddd', '2000-01-01 00:00:00'); +INSERT INTO `t5` VALUES (5, 'eeeeeee', '1999-12-01 00:00:00'); +INSERT INTO `t5` VALUES (6, 'fffffff', '2003-11-12 00:00:00'); +INSERT INTO `t5` VALUES (7, 'ggggggg', '1990-01-05 00:00:00'); +INSERT INTO `t5` VALUES (8, 'hhhhhhh', '1978-01-05 00:00:00'); +INSERT INTO `t5` VALUES (9, 'iiiiiii', '1979-01-05 00:00:00'); +INSERT INTO `t5` VALUES (10, 'jjjjjjj', '1992-01-05 00:00:00'); +INSERT INTO `t5` VALUES (11, 'kkkkkkk', '1993-01-05 00:00:00'); +INSERT INTO `t5` VALUES (12, 'mmmmmmm', '1994-01-05 00:00:00'); +INSERT INTO `t5` VALUES (13, 'nnnnnnn', '1989-01-05 00:00:00'); +INSERT INTO `t5` VALUES (14, 'ooooooo', '1983-12-05 00:00:00'); +INSERT INTO `t5` VALUES (15, 'ppppppp', '1986-06-05 00:00:00'); +INSERT INTO `t5` VALUES (16, 'qqqqqqq', '1974-04-11 00:00:00'); +INSERT INTO `t5` VALUES (17, 'qqqqqqq', '1960-03-15 00:00:00'); +INSERT INTO `t5` VALUES (18, 'sssssss', '1950-09-23 00:00:00'); +INSERT INTO `t5` VALUES (19, 'ttttttt', '1999-08-02 00:00:00'); +INSERT INTO `t5` VALUES (20, 'uuuuuuu', '1994-05-28 00:00:00'); +SELECT * FROM `t5`; +id name purchased +8 hhhhhhh 1978-01-05 +13 nnnnnnn 1989-01-05 +14 ooooooo 1983-12-05 +18 sssssss 1950-09-23 +3 ccccccc 1985-08-07 +9 iiiiiii 1979-01-05 +15 ppppppp 1986-06-05 +16 qqqqqqq 1974-04-11 +17 qqqqqqq 1960-03-15 +5 eeeeeee 1999-12-01 +12 mmmmmmm 1994-01-05 +7 ggggggg 1990-01-05 +10 jjjjjjj 1992-01-05 +11 kkkkkkk 1993-01-05 +19 ttttttt 1999-08-02 +20 uuuuuuu 1994-05-28 +2 bbbbbbb 2005-08-05 +6 fffffff 2003-11-12 +1 aaaaaaa 2006-01-05 +4 ddddddd 2000-01-01 +SELECT * FROM `t5` PARTITION(p0) ORDER BY id; +id name purchased +3 ccccccc 1985-08-07 +8 hhhhhhh 1978-01-05 +9 iiiiiii 1979-01-05 +13 nnnnnnn 1989-01-05 +14 ooooooo 1983-12-05 +15 ppppppp 1986-06-05 +16 qqqqqqq 1974-04-11 +17 qqqqqqq 1960-03-15 +18 sssssss 1950-09-23 +SELECT * FROM `t5` PARTITION(s0) ORDER BY id; +id name purchased +8 hhhhhhh 1978-01-05 +13 nnnnnnn 1989-01-05 +14 ooooooo 1983-12-05 +18 sssssss 1950-09-23 +SELECT * FROM `t5` PARTITION(s1) ORDER BY id; +id name purchased +3 ccccccc 1985-08-07 +9 iiiiiii 1979-01-05 +15 ppppppp 1986-06-05 +16 qqqqqqq 1974-04-11 +17 qqqqqqq 1960-03-15 +SELECT * FROM `t5` PARTITION(p1) ORDER BY id; +id name purchased +5 eeeeeee 1999-12-01 +7 ggggggg 1990-01-05 +10 jjjjjjj 1992-01-05 +11 kkkkkkk 1993-01-05 +12 mmmmmmm 1994-01-05 +19 ttttttt 1999-08-02 +20 uuuuuuu 1994-05-28 +SELECT * FROM `t5` PARTITION(s2) ORDER BY id; +id name purchased +5 eeeeeee 1999-12-01 +12 mmmmmmm 1994-01-05 +SELECT * FROM `t5` PARTITION(s3) ORDER BY id; +id name purchased +7 ggggggg 1990-01-05 +10 jjjjjjj 1992-01-05 +11 kkkkkkk 1993-01-05 +19 ttttttt 1999-08-02 +20 uuuuuuu 1994-05-28 +SELECT * FROM `t5` PARTITION(p2) ORDER BY id; +id name purchased +1 aaaaaaa 2006-01-05 +2 bbbbbbb 2005-08-05 +4 ddddddd 2000-01-01 +6 fffffff 2003-11-12 +SELECT * FROM `t5` PARTITION(s4) ORDER BY id; +id name purchased +2 bbbbbbb 2005-08-05 +6 fffffff 2003-11-12 +SELECT * FROM `t5` PARTITION(s5) ORDER BY id; +id name purchased +1 aaaaaaa 2006-01-05 +4 ddddddd 2000-01-01 +drop table t1,t2,t3,t4,t5; +create table t1 (a int) partition by hash(a) partitions 3; +insert into t1 values(1),(2),(3); +explain partitions select * from t1 where a=1; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 2 Using where +explain partitions select * from t1 partition (p1) where a=1; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 2 Using where +explain partitions select * from t1 partition (p1) where a=1 or a=2; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p1 ALL NULL NULL NULL NULL 2 Using where +explain partitions select * from t1 partition (p2) where a=1; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +drop table t1; +# +# Bug#59864: Crash if table empty: DELETE FROM t2 PARTITION (subp3). +# +CREATE TABLE t1 +(a INT NOT NULL, +b varchar (64), +INDEX (b,a), +PRIMARY KEY (a)) +PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) SUBPARTITIONS 3 +(PARTITION pNeg VALUES LESS THAN (0) +(SUBPARTITION subp0, +SUBPARTITION subp1, +SUBPARTITION subp2), +PARTITION `p0-29` VALUES LESS THAN (30) +(SUBPARTITION subp3, +SUBPARTITION subp4, +SUBPARTITION subp5), +PARTITION `p30-299` VALUES LESS THAN (300) +(SUBPARTITION subp6, +SUBPARTITION subp7, +SUBPARTITION subp8), +PARTITION `p300-2999` VALUES LESS THAN (3000) +(SUBPARTITION subp9, +SUBPARTITION subp10, +SUBPARTITION subp11), +PARTITION `p3000-299999` VALUES LESS THAN (300000) +(SUBPARTITION subp12, +SUBPARTITION subp13, +SUBPARTITION subp14)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) NOT NULL, + `b` varchar(64) DEFAULT NULL, + PRIMARY KEY (`a`), + KEY `b` (`b`,`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY RANGE (a) +SUBPARTITION BY HASH (a) +(PARTITION pNeg VALUES LESS THAN (0) + (SUBPARTITION subp0 ENGINE = InnoDB, + SUBPARTITION subp1 ENGINE = InnoDB, + SUBPARTITION subp2 ENGINE = InnoDB), + PARTITION `p0-29` VALUES LESS THAN (30) + (SUBPARTITION subp3 ENGINE = InnoDB, + SUBPARTITION subp4 ENGINE = InnoDB, + SUBPARTITION subp5 ENGINE = InnoDB), + PARTITION `p30-299` VALUES LESS THAN (300) + (SUBPARTITION subp6 ENGINE = InnoDB, + SUBPARTITION subp7 ENGINE = InnoDB, + SUBPARTITION subp8 ENGINE = InnoDB), + PARTITION `p300-2999` VALUES LESS THAN (3000) + (SUBPARTITION subp9 ENGINE = InnoDB, + SUBPARTITION subp10 ENGINE = InnoDB, + SUBPARTITION subp11 ENGINE = InnoDB), + PARTITION `p3000-299999` VALUES LESS THAN (300000) + (SUBPARTITION subp12 ENGINE = InnoDB, + SUBPARTITION subp13 ENGINE = InnoDB, + SUBPARTITION subp14 ENGINE = InnoDB)) */ +INSERT INTO t1 VALUES (-9, "negative nine"), (-8, "-8"), (-7, "-7"), (-6, "-6"), (-5, "-5"), (-4, "-4"), (-3, "-3"), (-2, "-2"), (-1, "-1"); +INSERT INTO t1 VALUES (9, "nine"), (8, "8"), (7, "7"), (6, "6"), (5, "5"), (4, "4"), (3, "3"), (2, "2"), (1, "1"); +INSERT INTO t1 VALUES (39, "Thirty nine"), (38, "38"), (37, "37"), (36, "36"), (35, "35"), (34, "34"), (33, "33"), (32, "32"), (31, "31"); +INSERT INTO t1 VALUES (339, "Three hundred thirty nine"), (338, "338"), (337, "337"), (336, "336"), (335, "335"), (334, "334"), (333, "333"), (332, "332"), (331, "331"); +INSERT INTO t1 VALUES (3339, "Three thousand three hundred thirty nine"), (3338, "3338"), (3337, "3337"), (3336, "3336"), (3335, "3335"), (3334, "3334"), (3333, "3333"), (3332, "3332"), (3331, "3331"); +SELECT * FROM t1; +a b +-1 -1 +-2 -2 +-3 -3 +-4 -4 +-5 -5 +-6 -6 +-7 -7 +-8 -8 +-9 negative nine +1 1 +2 2 +3 3 +31 31 +32 32 +33 33 +331 331 +332 332 +333 333 +3331 3331 +3332 3332 +3333 3333 +3334 3334 +3335 3335 +3336 3336 +3337 3337 +3338 3338 +3339 Three thousand three hundred thirty nine +334 334 +335 335 +336 336 +337 337 +338 338 +339 Three hundred thirty nine +34 34 +35 35 +36 36 +37 37 +38 38 +39 Thirty nine +4 4 +5 5 +6 6 +7 7 +8 8 +9 nine +SELECT * FROM t1 PARTITION (subp3); +a b +3 3 +6 6 +9 nine +DELETE FROM t1 PARTITION (subp3); +SELECT * FROM t1; +a b +-1 -1 +-2 -2 +-3 -3 +-4 -4 +-5 -5 +-6 -6 +-7 -7 +-8 -8 +-9 negative nine +1 1 +2 2 +31 31 +32 32 +33 33 +331 331 +332 332 +333 333 +3331 3331 +3332 3332 +3333 3333 +3334 3334 +3335 3335 +3336 3336 +3337 3337 +3338 3338 +3339 Three thousand three hundred thirty nine +334 334 +335 335 +336 336 +337 337 +338 338 +339 Three hundred thirty nine +34 34 +35 35 +36 36 +37 37 +38 38 +39 Thirty nine +4 4 +5 5 +7 7 +8 8 +SELECT * FROM t1 PARTITION (subp3); +a b +DELETE FROM t1 PARTITION (`p0-29`); +SELECT * FROM t1; +a b +-1 -1 +-2 -2 +-3 -3 +-4 -4 +-5 -5 +-6 -6 +-7 -7 +-8 -8 +-9 negative nine +31 31 +32 32 +33 33 +331 331 +332 332 +333 333 +3331 3331 +3332 3332 +3333 3333 +3334 3334 +3335 3335 +3336 3336 +3337 3337 +3338 3338 +3339 Three thousand three hundred thirty nine +334 334 +335 335 +336 336 +337 337 +338 338 +339 Three hundred thirty nine +34 34 +35 35 +36 36 +37 37 +38 38 +39 Thirty nine +SELECT * FROM t1 PARTITION (`p0-29`); +a b +ALTER TABLE t1 PARTITION BY HASH (a) PARTITIONS 3; +DELETE FROM t1 PARTITION (p2); +SELECT * FROM t1; +a b +-1 -1 +-3 -3 +-4 -4 +-6 -6 +-7 -7 +-9 negative nine +31 31 +33 33 +331 331 +333 333 +3331 3331 +3333 3333 +3334 3334 +3336 3336 +3337 3337 +3339 Three thousand three hundred thirty nine +334 334 +336 336 +337 337 +339 Three hundred thirty nine +34 34 +36 36 +37 37 +39 Thirty nine +SELECT * FROM t1 PARTITION (p2); +a b +DROP TABLE t1; +# +# Test explicit partition selection on a non partitioned temp table +# +CREATE TEMPORARY TABLE t1 (a INT); +SELECT * FROM t1 PARTITION(pNonexisting); +ERROR HY000: PARTITION () clause on non partitioned table +DROP TEMPORARY TABLE t1; +# +# Test CREATE LIKE does not take PARTITION clause +# +CREATE TABLE t1 (a INT) PARTITION BY HASH (a) PARTITIONS 3; +CREATE TABLE t2 LIKE t1 PARTITION (p0, p2); +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'PARTITION (p0, p2)' at line 1 +DROP TABLE t1; +SET @@default_storage_engine = @old_default_storage_engine; diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index a1accfa8e3d..2d35fe0bf07 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -31,6 +31,17 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 67 NULL 18 Using where; Using index DROP TABLE t1; # +# Bug#13007154: Crash in keys_to_use_for_scanning with ORDER BY +# and PARTITIONING +# +CREATE TABLE t1 (a INT, KEY(a)) +ENGINE = InnoDB +PARTITION BY KEY (a) PARTITIONS 1; +SELECT 1 FROM t1 WHERE a > (SELECT LAST_INSERT_ID() FROM t1 LIMIT 0) +ORDER BY a; +1 +DROP TABLE t1; +# # Bug#56287: crash when using Partition datetime in sub in query # CREATE TABLE t1 @@ -60,7 +71,7 @@ DROP TABLE t1; # Bug#54747: Deadlock between REORGANIZE PARTITION and # SELECT is not detected # -SET @old_innodb_thread_concurrency:= @@innodb_thread_concurrency; +SET @old_innodb_thread_concurrency := @@innodb_thread_concurrency; SET @old_innodb_thread_sleep_delay := @@innodb_thread_sleep_delay; SET GLOBAL innodb_thread_concurrency = 1; CREATE TABLE t1 diff --git a/mysql-test/r/partition_innodb_plugin.result b/mysql-test/r/partition_innodb_plugin.result index ceade2a793c..7a84745e611 100644 --- a/mysql-test/r/partition_innodb_plugin.result +++ b/mysql-test/r/partition_innodb_plugin.result @@ -76,18 +76,18 @@ t1.par SET innodb_strict_mode = OFF; ALTER TABLE t1 ADD PARTITION PARTITIONS 2; Warnings: -Warning 140 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. -Warning 140 InnoDB: ignoring KEY_BLOCK_SIZE=4. -Warning 140 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. -Warning 140 InnoDB: ignoring KEY_BLOCK_SIZE=4. -Warning 140 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. -Warning 140 InnoDB: ignoring KEY_BLOCK_SIZE=4. +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. +Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=4. +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. +Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=4. +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. +Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=4. t1.frm t1.par ALTER TABLE t1 REBUILD PARTITION p0; Warnings: -Warning 140 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. -Warning 140 InnoDB: ignoring KEY_BLOCK_SIZE=4. +Warning 1478 InnoDB: KEY_BLOCK_SIZE requires innodb_file_per_table. +Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=4. UNLOCK TABLES; SHOW CREATE TABLE t1; Table Create Table diff --git a/mysql-test/r/partition_mgm_err.result b/mysql-test/r/partition_mgm_err.result index a13278d724e..cbf45a2b7be 100644 --- a/mysql-test/r/partition_mgm_err.result +++ b/mysql-test/r/partition_mgm_err.result @@ -98,7 +98,7 @@ PARTITION BY KEY (a) (PARTITION x0, PARTITION x1); ALTER TABLE t1 ADD PARTITION PARTITIONS 0; ERROR HY000: At least one partition must be added -ALTER TABLE t1 ADD PARTITION PARTITIONS 1024; +ALTER TABLE t1 ADD PARTITION PARTITIONS 8192; ERROR HY000: Too many partitions (including subpartitions) were defined ALTER TABLE t1 DROP PARTITION x0; ERROR HY000: DROP PARTITION can only be used on RANGE/LIST partitions diff --git a/mysql-test/r/partition_myisam.result b/mysql-test/r/partition_myisam.result index 10586ddc548..80b3a9511ea 100644 --- a/mysql-test/r/partition_myisam.result +++ b/mysql-test/r/partition_myisam.result @@ -1,80 +1,62 @@ DROP TABLE IF EXISTS t1, t2; # -# Bug#50036: Inconsistent errors when using TIMESTAMP -# columns/expressions -# Added test with existing TIMESTAMP partitioning (when it was allowed). -CREATE TABLE t1 (a TIMESTAMP) -ENGINE = MyISAM -PARTITION BY HASH (UNIX_TIMESTAMP(a)); -INSERT INTO t1 VALUES ('2000-01-02 03:04:05'); -SELECT * FROM t1; -a -2000-01-02 03:04:05 -FLUSH TABLES; -# replacing t1.frm with TO_DAYS(a) which was allowed earlier. -# Disable warnings, since the result would differ when running with -# --ps-protocol (only for the 'SELECT * FROM t1' statement). -SELECT * FROM t1; -a -2000-01-02 03:04:05 +# BUG#11933226 - 60681: CHECKSUM TABLE RETURNS 0 FOR PARTITIONED TABLE +# +CREATE TABLE t1 ( +i INT +) +ENGINE=MyISAM +PARTITION BY RANGE (i) +(PARTITION p3 VALUES LESS THAN (3), +PARTITION p5 VALUES LESS THAN (5), +PARTITION pMax VALUES LESS THAN MAXVALUE); +INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6); +CHECKSUM TABLE t1; +Table Checksum +test.t1 2653438147 +ALTER TABLE t1 CHECKSUM = 1; +CHECKSUM TABLE t1 EXTENDED; +Table Checksum +test.t1 2653438147 +# Before patch this returned 0! +CHECKSUM TABLE t1; +Table Checksum +test.t1 2653438147 SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE= DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY HASH (TO_DAYS(a)) */ -INSERT INTO t1 VALUES ('2001-02-03 04:05:06'); -SELECT * FROM t1; -a -2000-01-02 03:04:05 -2001-02-03 04:05:06 -ALTER TABLE t1 ADD PARTITION PARTITIONS 2; -Warnings: -Warning 1486 Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed -ALTER TABLE t1 -PARTITION BY RANGE (TO_DAYS(a)) -(PARTITION p0 VALUES LESS THAN (10000), -PARTITION p1 VALUES LESS THAN (MAXVALUE)); -ERROR HY000: Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 CHECKSUM=1 +/*!50100 PARTITION BY RANGE (i) +(PARTITION p3 VALUES LESS THAN (3) ENGINE = MyISAM, + PARTITION p5 VALUES LESS THAN (5) ENGINE = MyISAM, + PARTITION pMax VALUES LESS THAN MAXVALUE ENGINE = MyISAM) */ +DROP TABLE t1; +# Same test without partitioning +CREATE TABLE t1 ( +i INT +) ENGINE=MyISAM; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + `i` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY HASH (TO_DAYS(a)) -PARTITIONS 3 */ -CREATE TABLE t2 LIKE t1; -SHOW CREATE TABLE t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY HASH (TO_DAYS(a)) -PARTITIONS 3 */ -Warnings: -Warning 1486 Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed -DROP TABLE t2; -CREATE TABLE t2 SELECT * FROM t1; -DROP TABLE t2; -ALTER TABLE t1 PARTITION BY HASH (UNIX_TIMESTAMP(a)); +INSERT INTO t1 VALUES (1), (2), (3), (4), (5), (6); +CHECKSUM TABLE t1; +Table Checksum +test.t1 2653438147 +ALTER TABLE t1 CHECKSUM = 1; +CHECKSUM TABLE t1 EXTENDED; +Table Checksum +test.t1 2653438147 +CHECKSUM TABLE t1; +Table Checksum +test.t1 2653438147 SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY HASH (UNIX_TIMESTAMP(a)) */ -ALTER TABLE t1 ADD PARTITION PARTITIONS 2; -SHOW CREATE TABLE t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY HASH (UNIX_TIMESTAMP(a)) -PARTITIONS 3 */ -SELECT * FROM t1; -a -2000-01-02 03:04:05 -2001-02-03 04:05:06 + `i` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 CHECKSUM=1 DROP TABLE t1; # # Bug#31931: Mix of handlers error message @@ -108,7 +90,7 @@ ERROR HY000: Failed to read from the .par file # Note that it is currently impossible to drop a partitioned table # without the .par file DROP TABLE t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' # # Bug#50392: insert_id is not reset for partitioned tables # auto_increment on duplicate entry @@ -247,3 +229,18 @@ PARTITION p1 VALUES LESS THAN (100) MAX_ROWS=100, PARTITION pMax VALUES LESS THAN MAXVALUE); INSERT INTO t1 VALUES (1, "Partition p1, first row"); DROP TABLE t1; +# +# bug#11760213-52599: ALTER TABLE REMOVE PARTITIONING ON NON-PARTITIONED +# TABLE CORRUPTS MYISAM +DROP TABLE if exists `t1`; +CREATE TABLE `t1`(`a` INT)ENGINE=myisam; +ALTER TABLE `t1` ADD COLUMN `b` INT; +CREATE UNIQUE INDEX `i1` ON `t1`(`b`); +CREATE UNIQUE INDEX `i2` ON `t1`(`a`); +ALTER TABLE `t1` ADD PRIMARY KEY (`a`); +ALTER TABLE `t1` REMOVE PARTITIONING; +ERROR HY000: Partition management on a not partitioned table is not possible +CHECK TABLE `t1` EXTENDED; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; diff --git a/mysql-test/r/partition_not_blackhole.result b/mysql-test/r/partition_not_blackhole.result index dc0339f8c48..923d70c0ad6 100644 --- a/mysql-test/r/partition_not_blackhole.result +++ b/mysql-test/r/partition_not_blackhole.result @@ -11,6 +11,6 @@ t1 SHOW CREATE TABLE t1; ERROR HY000: Incorrect information in file: './test/t1.frm' DROP TABLE t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' t1.frm t1.par diff --git a/mysql-test/r/partition_truncate.result b/mysql-test/r/partition_truncate.result index 66c0cd3d9da..7a82e47d818 100644 --- a/mysql-test/r/partition_truncate.result +++ b/mysql-test/r/partition_truncate.result @@ -5,7 +5,7 @@ partition by list (a) alter table t1 truncate partition p1,p1; ERROR HY000: Incorrect partition name alter table t1 truncate partition p0; -ERROR HY000: Incorrect partition name +ERROR HY000: Unknown partition 'p0' in table 't1' drop table t1; create table t1 (a int) partition by list (a) diff --git a/mysql-test/r/plugin.result b/mysql-test/r/plugin.result index 241e7a11ecc..62ae0e3747f 100644 --- a/mysql-test/r/plugin.result +++ b/mysql-test/r/plugin.result @@ -15,7 +15,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE STORAGE ENGINE PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.5 +PLUGIN_LIBRARY_VERSION 1.7 PLUGIN_AUTHOR Brian Aker, MySQL AB PLUGIN_DESCRIPTION Example storage engine PLUGIN_LICENSE GPL @@ -28,7 +28,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE DAEMON PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.5 +PLUGIN_LIBRARY_VERSION 1.7 PLUGIN_AUTHOR Sergei Golubchik PLUGIN_DESCRIPTION Unusable Daemon PLUGIN_LICENSE GPL @@ -60,7 +60,7 @@ PLUGIN_STATUS DELETED PLUGIN_TYPE STORAGE ENGINE PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.5 +PLUGIN_LIBRARY_VERSION 1.7 PLUGIN_AUTHOR Brian Aker, MySQL AB PLUGIN_DESCRIPTION Example storage engine PLUGIN_LICENSE GPL @@ -137,17 +137,29 @@ t1 CREATE TABLE `t1` ( ) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 `ULL`=10000000000000000000 `one_or_two`='ttt' `YESNO`=SSS `VAROPT`='5' #alter table alter table t1 ULL=10000000; +Warnings: +Note 1105 EXAMPLE DEBUG: ULL 4294967290 -> 10000000 show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL ) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 `one_or_two`='ttt' `YESNO`=SSS `VAROPT`='5' `ULL`=10000000 alter table t1 change a a int complex='c,c,c'; +Warnings: +Note 1105 EXAMPLE DEBUG: Field `a` COMPLEX '(null)' -> 'c,c,c' show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL `complex`='c,c,c' ) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 `one_or_two`='ttt' `YESNO`=SSS `VAROPT`='5' `ULL`=10000000 +alter table t1 one_or_two=two; +Warnings: +Note 1105 EXAMPLE DEBUG: Field `a` COMPLEX 'c,c,c' -> 'c,c,c' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL `complex`='c,c,c' +) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 `YESNO`=SSS `VAROPT`='5' `ULL`=10000000 `one_or_two`=two drop table t1; #illegal value error SET SQL_MODE=''; @@ -168,6 +180,8 @@ select create_options from information_schema.tables where table_schema='test' a create_options `ULL`=4660 `VAROPT`='5' ALTER TABLE t1 ULL=DEFAULT; +Warnings: +Note 1105 EXAMPLE DEBUG: ULL 4660 -> 4294967295 SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -188,6 +202,8 @@ t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL ) ENGINE=EXAMPLE DEFAULT CHARSET=latin1 `varopt`=15 alter table t1 varopt=default; +Warnings: +Note 1105 EXAMPLE DEBUG: Field `a` COMPLEX '(null)' -> '(null)' show create table t1; Table Create Table t1 CREATE TABLE `t1` ( diff --git a/mysql-test/r/profiling.result b/mysql-test/r/profiling.result index 6292cd085e4..4c531a8a5f7 100644 --- a/mysql-test/r/profiling.result +++ b/mysql-test/r/profiling.result @@ -123,8 +123,8 @@ select query_id, count(*), sum(duration) from information_schema.profiling group select CPU_user, CPU_system, Context_voluntary, Context_involuntary, Block_ops_in, Block_ops_out, Messages_sent, Messages_received, Page_faults_major, Page_faults_minor, Swaps, Source_function, Source_file, Source_line from information_schema.profiling; drop table if exists t1, t2, t3; Warnings: -Note 1051 Unknown table 't2' -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t2' +Note 1051 Unknown table 'test.t3' create table t1 (id int ); create table t2 (id int not null); create table t3 (id int not null primary key); @@ -309,7 +309,7 @@ select @@profiling; set session profiling = OFF; drop table if exists profile_log; Warnings: -Note 1051 Unknown table 'profile_log' +Note 1051 Unknown table 'test.profile_log' create table profile_log (how_many int); drop procedure if exists p1; drop procedure if exists p2; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 95217d9716a..f6a2a16f038 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -51,7 +51,7 @@ execute stmt4; prepare stmt4 from 'drop table t2'; execute stmt4; execute stmt4; -ERROR 42S02: Unknown table 't2' +ERROR 42S02: Unknown table 'test.t2' prepare stmt5 from 'select ? + a from t1'; set @a=1; execute stmt5 using @a; @@ -526,7 +526,7 @@ FOUND_ROWS() deallocate prepare stmt; drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' create table t1 (c1 int(11) not null, c2 int(11) not null, primary key (c1,c2), key c2 (c2), key c1 (c1)); insert into t1 values (200887, 860); @@ -1201,13 +1201,13 @@ SET @aux= "SELECT COUNT(*) prepare my_stmt from @aux; execute my_stmt; COUNT(*) -42 +43 execute my_stmt; COUNT(*) -42 +43 execute my_stmt; COUNT(*) -42 +43 deallocate prepare my_stmt; drop procedure if exists p1| drop table if exists t1| @@ -2799,48 +2799,48 @@ drop table if exists t2; create procedure proc_1() show warnings; drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' call proc_1(); Level Code Message -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' call proc_1(); Level Code Message -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' drop table if exists t1, t2; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' call proc_1(); Level Code Message -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' drop procedure proc_1; create function func_1() returns int begin show warnings; return 1; end| ERROR 0A000: Not allowed to return a result set from a function prepare abc from "show warnings"; drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' execute abc; Level Code Message -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' drop table if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' execute abc; Level Code Message -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' drop table if exists t1, t2; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' execute abc; Level Code Message -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' deallocate prepare abc; set @my_password="password"; set @my_data="clear text to encode"; @@ -2926,7 +2926,7 @@ i j DROP TABLE t1, t2; drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' prepare stmt from "create table t1 (c char(100) character set utf8, key (c(10)))"; execute stmt; diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result index 0a8aea94e8a..9981156bc5f 100644 --- a/mysql-test/r/ps_1general.result +++ b/mysql-test/r/ps_1general.result @@ -325,10 +325,10 @@ drop table if exists t5; prepare stmt1 from ' drop table if exists t5 ' ; execute stmt1 ; Warnings: -Note 1051 Unknown table 't5' +Note 1051 Unknown table 'test.t5' prepare stmt1 from ' drop table t5 ' ; execute stmt1 ; -ERROR 42S02: Unknown table 't5' +ERROR 42S02: Unknown table 'test.t5' prepare stmt1 from ' SELECT @@version ' ; execute stmt1 ; @@version diff --git a/mysql-test/r/ps_ddl1.result b/mysql-test/r/ps_ddl1.result index 87abcd90590..667cbed8a7a 100644 --- a/mysql-test/r/ps_ddl1.result +++ b/mysql-test/r/ps_ddl1.result @@ -420,7 +420,7 @@ call p_verify_reprepare_count(0); SUCCESS execute stmt; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' call p_verify_reprepare_count(0); SUCCESS diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 62f8a9728c3..18f9db1743d 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1944,6 +1944,41 @@ COUNT(*) DROP TABLE t1; SET GLOBAL query_cache_size= @qc; # +End of 5.5 tests +# +# MDEV-617 LP:671189 - Query cache is not used for tables or +# databases with dots in their names +# +CREATE DATABASE `foo.bar`; +use `foo.bar`; +flush status; +CREATE TABLE moocow (a int); +INSERT INTO moocow VALUES (1), (2), (3); +SHOW STATUS LIKE 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 0 +SELECT * FROM moocow; +a +1 +2 +3 +SHOW STATUS LIKE 'Qcache_inserts'; +Variable_name Value +Qcache_inserts 1 +SHOW STATUS LIKE 'Qcache_hits'; +Variable_name Value +Qcache_hits 0 +SELECT * FROM moocow; +a +1 +2 +3 +SHOW STATUS LIKE 'Qcache_hits'; +Variable_name Value +Qcache_hits 1 +use test; +drop database `foo.bar`; +End of 10.0 tests restore defaults SET GLOBAL query_cache_type= default; SET GLOBAL query_cache_size= default; diff --git a/mysql-test/r/read_only.result b/mysql-test/r/read_only.result index 3811c6c5487..c9c569137b2 100644 --- a/mysql-test/r/read_only.result +++ b/mysql-test/r/read_only.result @@ -117,10 +117,10 @@ select @@global.read_only; 1 unlock tables; drop temporary table ttt; -ERROR 42S02: Unknown table 'ttt' +ERROR 42S02: Unknown table 'test.ttt' drop temporary table if exists ttt; Warnings: -Note 1051 Unknown table 'ttt' +Note 1051 Unknown table 'test.ttt' connection default; set global read_only=0; drop table t1,t2; diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result index 7433ab8a0c9..74370ba74dd 100644 --- a/mysql-test/r/rename.result +++ b/mysql-test/r/rename.result @@ -39,7 +39,7 @@ select * from t3; 3 table 3 drop table if exists t1,t2,t3,t4; Warnings: -Note 1051 Unknown table 't4' +Note 1051 Unknown table 'test.t4' CREATE TABLE t1 (a int); CREATE TABLE t3 (a int); FLUSH TABLES WITH READ LOCK; diff --git a/mysql-test/r/row-checksum-old.result b/mysql-test/r/row-checksum-old.result index 87f0bb8af2d..ef523463860 100644 --- a/mysql-test/r/row-checksum-old.result +++ b/mysql-test/r/row-checksum-old.result @@ -73,7 +73,7 @@ test.t1 4108368782 drop table if exists t1; create table t1 (a int null, v varchar(100)) engine=innodb checksum=0 row_format=fixed; Warnings: -Warning 140 InnoDB: assuming ROW_FORMAT=COMPACT. +Warning 1478 InnoDB: assuming ROW_FORMAT=COMPACT. insert into t1 values(null, null), (1, "hello"); checksum table t1; Table Checksum diff --git a/mysql-test/r/row-checksum.result b/mysql-test/r/row-checksum.result index 9e58d6fa96e..fb8a1260a1d 100644 --- a/mysql-test/r/row-checksum.result +++ b/mysql-test/r/row-checksum.result @@ -73,7 +73,7 @@ test.t1 3885665021 drop table if exists t1; create table t1 (a int null, v varchar(100)) engine=innodb checksum=0 row_format=fixed; Warnings: -Warning 140 InnoDB: assuming ROW_FORMAT=COMPACT. +Warning 1478 InnoDB: assuming ROW_FORMAT=COMPACT. insert into t1 values(null, null), (1, "hello"); checksum table t1; Table Checksum diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index c086a62275a..d639580acf9 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -4175,18 +4175,24 @@ str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01 00:00:00' set SQL_MODE=TRADITIONAL; select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' 0 select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34' -0 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00' for function str_to_date set SQL_MODE=DEFAULT; select str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20' diff --git a/mysql-test/r/select_jcl6.result b/mysql-test/r/select_jcl6.result index 00b356fc1c0..18b050b53e7 100644 --- a/mysql-test/r/select_jcl6.result +++ b/mysql-test/r/select_jcl6.result @@ -4186,18 +4186,24 @@ str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01 00:00:00' set SQL_MODE=TRADITIONAL; select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' 0 select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34' -0 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00' for function str_to_date set SQL_MODE=DEFAULT; select str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20' diff --git a/mysql-test/r/select_pkeycache.result b/mysql-test/r/select_pkeycache.result index c086a62275a..d639580acf9 100644 --- a/mysql-test/r/select_pkeycache.result +++ b/mysql-test/r/select_pkeycache.result @@ -4175,18 +4175,24 @@ str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01 00:00:00' set SQL_MODE=TRADITIONAL; select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34'; str_to_date('2007-10-01 12:34','%Y-%m-%d %H:%i') = '2007-10-00 12:34' 0 select str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34'; str_to_date('2007-10-00 12:34','%Y-%m-%d %H:%i') = '2007-10-01 12:34' -0 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00 12:34' for function str_to_date select str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '2007/09/01' and '2007/10/20' -1 +NULL +Warnings: +Warning 1411 Incorrect datetime value: '2007-10-00' for function str_to_date set SQL_MODE=DEFAULT; select str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20'; str_to_date('2007-10-00','%Y-%m-%d') between '' and '2007/10/20' diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 44a37b9a9e5..0e4cf6c6775 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -198,7 +198,7 @@ show create table t2; Table Create Table t2 CREATE TEMPORARY TABLE `t2` ( `a` int(11) NOT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t2; create table t1 ( test_set set( 'val1', 'val2', 'val3' ) not null default '', diff --git a/mysql-test/r/signal.result b/mysql-test/r/signal.result index 062b866475d..a5eb24442b4 100644 --- a/mysql-test/r/signal.result +++ b/mysql-test/r/signal.result @@ -1191,8 +1191,6 @@ end $$ call test_signal() $$ Caught by SQLSTATE Caught by SQLSTATE -Warnings: -Warning 1012 Raising a warning drop procedure test_signal $$ create procedure test_signal() begin @@ -1208,8 +1206,6 @@ end $$ call test_signal() $$ Caught by number Caught by number -Warnings: -Warning 1012 Raising a warning drop procedure test_signal $$ create procedure test_signal() begin @@ -1225,8 +1221,6 @@ end $$ call test_signal() $$ Caught by SQLWARNING Caught by SQLWARNING -Warnings: -Warning 1012 Raising a warning drop procedure test_signal $$ create procedure test_signal() begin @@ -1242,8 +1236,6 @@ end $$ call test_signal() $$ Caught by SQLSTATE Caught by SQLSTATE -Warnings: -Error 1012 Raising a not found drop procedure test_signal $$ create procedure test_signal() begin @@ -1259,8 +1251,6 @@ end $$ call test_signal() $$ Caught by number Caught by number -Warnings: -Error 1012 Raising a not found drop procedure test_signal $$ create procedure test_signal() begin @@ -1276,8 +1266,6 @@ end $$ call test_signal() $$ Caught by NOT FOUND Caught by NOT FOUND -Warnings: -Error 1012 Raising a not found drop procedure test_signal $$ create procedure test_signal() begin @@ -1293,8 +1281,6 @@ end $$ call test_signal() $$ Caught by SQLSTATE Caught by SQLSTATE -Warnings: -Error 1012 Raising an error drop procedure test_signal $$ create procedure test_signal() begin @@ -1310,8 +1296,6 @@ end $$ call test_signal() $$ Caught by number Caught by number -Warnings: -Error 1012 Raising an error drop procedure test_signal $$ create procedure test_signal() begin @@ -1327,25 +1311,29 @@ end $$ call test_signal() $$ Caught by SQLEXCEPTION Caught by SQLEXCEPTION -Warnings: -Error 1012 Raising an error drop procedure test_signal $$ # # Test where SIGNAL can be used # + +# RETURN statement clears Diagnostics Area, thus +# the warnings raised in a stored function are not +# visible outsidef the stored function. So, we're using +# @@warning_count variable to check that SIGNAL succeeded. + create function test_signal_func() returns integer begin +DECLARE v INT; DECLARE warn CONDITION FOR SQLSTATE "01XXX"; SIGNAL warn SET MESSAGE_TEXT = "This function SIGNAL a warning", MYSQL_ERRNO = 1012; -return 5; +SELECT @@warning_count INTO v; +return v; end $$ select test_signal_func() $$ test_signal_func() -5 -Warnings: -Warning 1012 This function SIGNAL a warning +1 drop function test_signal_func $$ create function test_signal_func() returns integer begin @@ -1468,7 +1456,6 @@ after RESIGNAL after RESIGNAL Warnings: Warning 1012 Raising a warning -Warning 1012 Raising a warning drop procedure test_resignal $$ create procedure test_resignal() begin @@ -1523,7 +1510,6 @@ after RESIGNAL after RESIGNAL Warnings: Warning 1264 Out of range value for column 'a' at row 1 -Warning 1264 Out of range value for column 'a' at row 1 drop procedure test_resignal $$ create procedure test_resignal() begin @@ -1557,7 +1543,7 @@ end $$ call test_resignal() $$ before RESIGNAL before RESIGNAL -ERROR 42S02: Unknown table 'no_such_table' +ERROR 42S02: Unknown table 'test.no_such_table' drop procedure test_resignal $$ create procedure test_resignal() begin @@ -1580,7 +1566,6 @@ before RESIGNAL after RESIGNAL after RESIGNAL Warnings: -Warning 1012 Raising a warning Warning 5555 RESIGNAL of a warning drop procedure test_resignal $$ create procedure test_resignal() @@ -1641,7 +1626,6 @@ before RESIGNAL after RESIGNAL after RESIGNAL Warnings: -Warning 1264 Out of range value for column 'a' at row 1 Warning 5555 RESIGNAL of a warning drop procedure test_resignal $$ create procedure test_resignal() @@ -2054,7 +2038,7 @@ before RESIGNAL after RESIGNAL after RESIGNAL Warnings: -Error 1051 Unknown table 'no_such_table' +Error 1051 Unknown table 'test.no_such_table' Warning 5555 RESIGNAL to a warning drop procedure test_resignal $$ create procedure test_resignal() @@ -2075,7 +2059,7 @@ before RESIGNAL ERROR 02444: RESIGNAL to a not found show warnings $$ Level Code Message -Error 1051 Unknown table 'no_such_table' +Error 1051 Unknown table 'test.no_such_table' Error 5555 RESIGNAL to a not found drop procedure test_resignal $$ create procedure test_resignal() @@ -2096,7 +2080,7 @@ before RESIGNAL ERROR 44444: RESIGNAL to an error show warnings $$ Level Code Message -Error 1051 Unknown table 'no_such_table' +Error 1051 Unknown table 'test.no_such_table' Error 5555 RESIGNAL to an error drop procedure test_resignal $$ # @@ -2143,9 +2127,6 @@ CALL peter_p2() $$ ERROR 42000: Hi, I am a useless error message show warnings $$ Level Code Message -Error 1231 Variable 'sql_mode' can't be set to the value of 'NULL' -Error 1231 Variable 'sql_mode' can't be set to the value of 'NULL' -Error 9999 Variable 'sql_mode' can't be set to the value of 'NULL' Error 9999 Hi, I am a useless error message drop procedure peter_p1 $$ drop procedure peter_p2 $$ diff --git a/mysql-test/r/signal_demo2.result b/mysql-test/r/signal_demo2.result index 223030b0624..5c8ac328a4a 100644 --- a/mysql-test/r/signal_demo2.result +++ b/mysql-test/r/signal_demo2.result @@ -74,7 +74,7 @@ In proc_bottom() In proc_bottom() Doing something that fail (simulate an error) ... Doing something that fail (simulate an error) ... -ERROR 42S02: Unknown table 'no_such_table' +ERROR 42S02: Unknown table 'demo.no_such_table' call proc_top_a(3); Starting ... Starting ... @@ -167,7 +167,7 @@ Doing something that fail (simulate an error) ... Doing something that fail (simulate an error) ... Doing cleanup ! Doing cleanup ! -ERROR 42S02: Unknown table 'no_such_table' +ERROR 42S02: Unknown table 'demo.no_such_table' call proc_top_b(3); Starting ... Starting ... diff --git a/mysql-test/r/signal_demo3.result b/mysql-test/r/signal_demo3.result index a89ce703d20..cc7042269bb 100644 --- a/mysql-test/r/signal_demo3.result +++ b/mysql-test/r/signal_demo3.result @@ -77,7 +77,7 @@ call proc_1(); ERROR 45000: Oops in proc_1 show warnings; Level Code Message -Error 1051 Unknown table 'oops_it_is_not_here' +Error 1051 Unknown table 'demo.oops_it_is_not_here' Error 1644 Oops in proc_9 Error 1644 Oops in proc_8 Error 1644 Oops in proc_7 @@ -95,11 +95,11 @@ call proc_1(); ERROR 45000: Oops in proc_1 show warnings; Level Code Message -Error 1051 Unknown table 'oops_it_is_not_here' -Error 1644 Oops in proc_9 -Error 1644 Oops in proc_8 -Error 1644 Oops in proc_7 -Error 1644 Oops in proc_6 +Error 1644 Oops in proc_5 +Error 1644 Oops in proc_4 +Error 1644 Oops in proc_3 +Error 1644 Oops in proc_2 +Error 1644 Oops in proc_1 SET @@session.max_error_count = 7; SELECT @@session.max_error_count; @@session.max_error_count @@ -108,13 +108,13 @@ call proc_1(); ERROR 45000: Oops in proc_1 show warnings; Level Code Message -Error 1051 Unknown table 'oops_it_is_not_here' -Error 1644 Oops in proc_9 -Error 1644 Oops in proc_8 Error 1644 Oops in proc_7 Error 1644 Oops in proc_6 Error 1644 Oops in proc_5 Error 1644 Oops in proc_4 +Error 1644 Oops in proc_3 +Error 1644 Oops in proc_2 +Error 1644 Oops in proc_1 SET @@session.max_error_count = 9; SELECT @@session.max_error_count; @@session.max_error_count @@ -123,7 +123,6 @@ call proc_1(); ERROR 45000: Oops in proc_1 show warnings; Level Code Message -Error 1051 Unknown table 'oops_it_is_not_here' Error 1644 Oops in proc_9 Error 1644 Oops in proc_8 Error 1644 Oops in proc_7 @@ -132,6 +131,7 @@ Error 1644 Oops in proc_5 Error 1644 Oops in proc_4 Error 1644 Oops in proc_3 Error 1644 Oops in proc_2 +Error 1644 Oops in proc_1 drop database demo; SET @@global.max_error_count = @start_global_value; SELECT @@global.max_error_count; diff --git a/mysql-test/r/sp-big.result b/mysql-test/r/sp-big.result index d28b7004330..9765508859c 100644 --- a/mysql-test/r/sp-big.result +++ b/mysql-test/r/sp-big.result @@ -46,8 +46,6 @@ end while; close cur1; end| call p1(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select count(*) from t1; count(*) 256 diff --git a/mysql-test/r/sp-bugs.result b/mysql-test/r/sp-bugs.result index 9d9deaebcc3..e34f8f9e63a 100644 --- a/mysql-test/r/sp-bugs.result +++ b/mysql-test/r/sp-bugs.result @@ -43,8 +43,6 @@ END| SELECT f2 (); f2 () NULL -Warnings: -Error 1305 FUNCTION testdb.f_not_exists does not exist DROP SCHEMA testdb; USE test; # @@ -134,6 +132,15 @@ DROP DATABASE testdb; USE test; End of 5.1 tests # +# BUG#13489996 valgrind:conditional jump or move depends on +# uninitialised values-field_blob +# +CREATE FUNCTION sf() RETURNS BLOB RETURN ""; +SELECT sf(); +sf() + +DROP FUNCTION sf; +# # Bug#11763507 - 56224: FUNCTION NAME IS CASE-SENSITIVE # SET @@SQL_MODE = ''; diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index 3bead4fc826..c9d2f7b023a 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -711,8 +711,6 @@ looping i looping 1 looping i looping 0 -Warnings: -Error 1062 Duplicate entry '1' for key 'a' call proc_26977_works(2); do something do something @@ -732,8 +730,6 @@ looping i looping 0 optimizer: keep hreturn optimizer: keep hreturn -Warnings: -Error 1062 Duplicate entry '2' for key 'a' drop table t1; drop procedure proc_26977_broken; drop procedure proc_26977_works; diff --git a/mysql-test/r/sp-destruct.result b/mysql-test/r/sp-destruct.result index 6d85c3ce496..172e40cb40c 100644 --- a/mysql-test/r/sp-destruct.result +++ b/mysql-test/r/sp-destruct.result @@ -149,7 +149,7 @@ alter table mysql.proc drop column type; # The below statement should not cause assertion failure. drop database mysqltest; Warnings: -Error 1728 Column count of mysql.proc is wrong. Expected 20, found 19. The table is probably corrupted +Error 1805 Column count of mysql.proc is wrong. Expected 20, found 19. The table is probably corrupted # Restore mysql.proc. drop table mysql.proc; # @@ -166,7 +166,7 @@ CREATE PROCEDURE db1.p1() SET @foo = 10; ALTER TABLE mysql.proc MODIFY comment CHAR (32); DROP DATABASE db1; Warnings: -Error 1729 Cannot load from mysql.proc. The table is probably corrupted +Error 1728 Cannot load from mysql.proc. The table is probably corrupted # Restore mysql.proc DROP TABLE mysql.proc; RENAME TABLE proc_backup TO mysql.proc; diff --git a/mysql-test/r/sp-dynamic.result b/mysql-test/r/sp-dynamic.result index cdfeb8ab020..7309ba4c765 100644 --- a/mysql-test/r/sp-dynamic.result +++ b/mysql-test/r/sp-dynamic.result @@ -249,7 +249,7 @@ drop procedure p1| drop table if exists t1| drop table if exists t2| Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' create table t1 (id integer primary key auto_increment, stmt_text char(35), status varchar(20))| insert into t1 (stmt_text) values diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 24cbb945fd2..71fab8c9654 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -466,7 +466,7 @@ drop table t5; end| create table t5 (x int)| call bug3294()| -ERROR 42S02: Unknown table 't5' +ERROR 42S02: Unknown table 'test.t5' drop procedure bug3294| drop procedure if exists bug8776_1| drop procedure if exists bug8776_2| @@ -1344,8 +1344,6 @@ set @in_func := 0; select func_20713_a(); func_20713_a() NULL -Warnings: -Error 1146 Table 'test.bogus_table_20713' doesn't exist select @in_func; @in_func 2 @@ -1353,8 +1351,6 @@ set @in_func := 0; select func_20713_b(); func_20713_b() NULL -Warnings: -Error 1146 Table 'test.bogus_table_20713' doesn't exist select @in_func; @in_func 2 @@ -1567,7 +1563,7 @@ f2() 1 drop function f2; drop table t2; -ERROR 42S02: Unknown table 't2' +ERROR 42S02: Unknown table 'test.t2' End of 5.1 tests drop procedure if exists proc_33983_a; drop procedure if exists proc_33983_b; @@ -1821,11 +1817,8 @@ CAST('10 ' as unsigned integer) c 3 @@warning_count -1 +0 Level Code Message -Warning 1292 Truncated incorrect INTEGER value: '10 ' -Warnings: -Warning 1292 Truncated incorrect INTEGER value: '10 ' CALL p6(); CAST('10 ' as unsigned integer) 10 @@ -1833,8 +1826,6 @@ Level Code Message Warning 1292 Truncated incorrect INTEGER value: '10 ' c 1 -Warnings: -Warning 1292 Truncated incorrect INTEGER value: '10 ' DROP PROCEDURE p1; DROP PROCEDURE p2; DROP PROCEDURE p3; @@ -1885,9 +1876,6 @@ END| CALL p1(); exception exception -Warnings: -Warning 1292 Truncated incorrect INTEGER value: '10 ' -Error 1048 Column 'b' cannot be null DROP TABLE t1; DROP PROCEDURE p1; # @@ -1931,11 +1919,8 @@ CALL p1(); NULL warning caught (expected) warning caught (expected) -Warnings: -Warning 1365 Division by 0 SHOW WARNINGS; Level Code Message -Warning 1365 Division by 0 CALL p2(); 5 / 0 NULL @@ -2008,3 +1993,878 @@ Error 1048 Column 'c' cannot be null DROP TABLE t1; DROP TABLE t2; DROP PROCEDURE p1; + +################################################################### +# Tests for the following bugs: +# - Bug#11763171: 55852 - Possibly inappropriate handler activation. +# - Bug#11749343: 38806 - Wrong scope for SQL HANDLERS in SP. +################################################################### + + +# -- Check that SQL-conditions thrown by Statement-blocks are +# -- handled by Handler-decl blocks properly. + +CREATE PROCEDURE p1() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H2' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should be handled by H2. +END| + +CALL p1()| +HandlerId +H2 + +# -- Check that SQL-conditions thrown by Statement-blocks are +# -- handled by Handler-decl blocks properly in case of nested +# -- SQL-blocks. + +CREATE PROCEDURE p2() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H2' AS HandlerId; +BEGIN +SELECT 'B1' AS BlockId; +BEGIN +SELECT 'B2' AS BlockId; +BEGIN +SELECT 'B3' AS BlockId; +SIGNAL SQLSTATE '01000'; # Should be handled by H2. +END; +END; +END; +END| + +CALL p2()| +BlockId +B1 +BlockId +B2 +BlockId +B3 +HandlerId +H2 + +# -- Check SQL-handler resolution rules. + +CREATE PROCEDURE p3() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'H3' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should be handled by H3. +END| + +CALL p3()| +HandlerId +H3 + +CREATE PROCEDURE p4() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'H2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H3' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should be handled by H2. +END| + +CALL p4()| +HandlerId +H2 + +CREATE PROCEDURE p5() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'H2' AS HandlerId; +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H3' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should be handled by H3. +END; +END| + +CALL p5()| +HandlerId +H3 + +# -- Check that handlers don't handle its own exceptions. + +CREATE PROCEDURE p6() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +SELECT 'H1' AS HandlerId; +SIGNAL SQLSTATE 'HY000'; # Should *not* be handled by H1. +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # Should be handled by H1. +END| + +CALL p6()| +SignalId +S1 +HandlerId +H1 +ERROR HY000: Unhandled user-defined exception condition + +# -- Check that handlers don't handle its own warnings. + +CREATE PROCEDURE p7() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +SELECT 'H1' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1. +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE '01000'; # Should be handled by H1. +END| + +CALL p7()| +SignalId +S1 +HandlerId +H1 +Warnings: +Warning 1642 Unhandled user-defined warning condition + +# -- Check that conditions for handlers are not handled by the handlers +# -- from the same block. + +CREATE PROCEDURE p8() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +SELECT 'H2' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1. +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # Should be handled by H2. +END| + +CALL p8()| +SignalId +S1 +HandlerId +H2 +Warnings: +Warning 1642 Unhandled user-defined warning condition + +# -- Check that conditions for handlers are not handled by the handlers +# -- from the same block even if they are thrown deep down the stack. + +CREATE PROCEDURE p9() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H1:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H1:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H2:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H2:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H3:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H3:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H4:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H4:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H5:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H5:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H6:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H6:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +SELECT 'H2' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should *not* be handled by H1. +END; +SELECT 'S6' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S5' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S4' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S3' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S2' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # Should be handled by H2. +END| + +CALL p9()| +SignalId +S1 +SignalId +S2 +SignalId +S3 +SignalId +S4 +SignalId +S5 +SignalId +S6 +HandlerId +H2 +Warnings: +Warning 1642 Unhandled user-defined warning condition + +# -- Check that handlers are choosen properly in case of deep stack and +# -- nested SQL-blocks. + +CREATE PROCEDURE p10() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H2' AS HandlerId; +BEGIN +BEGIN +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H1:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H1:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H2:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H2:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H3:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H3:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H4:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H4:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H5:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H5:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000' + SELECT 'Wrong:H6:1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'Wrong:H6:2' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +BEGIN +SELECT 'H2' AS HandlerId; +SIGNAL SQLSTATE '01000'; # Should be handled by H1. +END; +SELECT 'S6' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S5' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S4' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S3' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S2' AS SignalId; +SIGNAL SQLSTATE 'HY000'; +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # Should be handled by H2. +END; +END; +END; +END| + +CALL p10()| +SignalId +S1 +SignalId +S2 +SignalId +S3 +SignalId +S4 +SignalId +S5 +SignalId +S6 +HandlerId +H2 +HandlerId +H1 + +# -- Test stored procedure from Peter's mail. + +CREATE PROCEDURE p11() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H1' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H2' AS HandlerId; +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01000', 1249 +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION +SELECT 'H3' AS HandlerId; +DECLARE CONTINUE HANDLER FOR SQLWARNING +SELECT 'H4' AS HandlerId; +BEGIN +SELECT 'H5' AS HandlerId; +SELECT 'S3' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # H3 +SELECT 'S4' AS SignalId; +SIGNAL SQLSTATE '22003'; # H3 +SELECT 'S5' AS SignalId; +SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H4 +END; +END; +SELECT 'S6' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # H1 +SELECT 'S7' AS SignalId; +SIGNAL SQLSTATE '22003'; # H1 +SELECT 'S8' AS SignalId; +SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H5 +END; +SELECT 'S1' AS SignalId; +SIGNAL SQLSTATE 'HY000'; # H1 +SELECT 'S2' AS SignalId; +SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1249; # H2 +END| + +CALL p11()| +SignalId +S6 +HandlerId +H1 +SignalId +S7 +HandlerId +H1 +SignalId +S8 +HandlerId +H5 +SignalId +S3 +HandlerId +H3 +SignalId +S4 +HandlerId +H3 +SignalId +S5 +HandlerId +H4 +SignalId +S1 +HandlerId +H1 +SignalId +S2 +HandlerId +H2 + +# -- Check that runtime stack-trace can be deeper than parsing-time one. + +CREATE PROCEDURE p12() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01001' + BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01001' + BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01001' + BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01001' + BEGIN +DECLARE CONTINUE HANDLER FOR SQLSTATE '01001' + BEGIN +SELECT 'H1:5' AS HandlerId; +SIGNAL SQLSTATE '01002'; +END; +SELECT 'H1:4' AS HandlerId; +SIGNAL SQLSTATE '01001'; +END; +SELECT 'H1:3' AS HandlerId; +SIGNAL SQLSTATE '01001'; +END; +SELECT 'H1:2' AS HandlerId; +SIGNAL SQLSTATE '01001'; +END; +SELECT 'H1:1' AS HandlerId; +SIGNAL SQLSTATE '01001'; +END; +######################################################### +DECLARE CONTINUE HANDLER FOR SQLSTATE '01002' + SELECT 'OK' AS Msg; +######################################################### +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +SELECT 'H2:5' AS HandlerId; +SIGNAL SQLSTATE '01001'; +END; +SELECT 'H2:4' AS HandlerId; +SIGNAL SQLSTATE '01000'; +END; +SELECT 'H2:3' AS HandlerId; +SIGNAL SQLSTATE '01000'; +END; +SELECT 'H2:2' AS HandlerId; +SIGNAL SQLSTATE '01000'; +END; +SELECT 'H2:1' AS HandlerId; +SIGNAL SQLSTATE '01000'; +END; +####################################################### +SELECT 'Throw 01000' AS Msg; +SIGNAL SQLSTATE '01000'; +END; +END| + +CALL p12()| +Msg +Throw 01000 +HandlerId +H2:1 +HandlerId +H2:2 +HandlerId +H2:3 +HandlerId +H2:4 +HandlerId +H2:5 +HandlerId +H1:1 +HandlerId +H1:2 +HandlerId +H1:3 +HandlerId +H1:4 +HandlerId +H1:5 +Warnings: +Warning 1642 Unhandled user-defined warning condition + +# -- Check that handler-call-frames are removed properly for EXIT +# -- handlers. + +CREATE PROCEDURE p13() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING +BEGIN +DECLARE EXIT HANDLER FOR SQLWARNING +BEGIN +SELECT 'EXIT handler 3' AS Msg; +END; +SELECT 'CONTINUE handler 2: 1' AS Msg; +SIGNAL SQLSTATE '01000'; +SELECT 'CONTINUE handler 2: 2' AS Msg; +END; +SELECT 'CONTINUE handler 1: 1' AS Msg; +SIGNAL SQLSTATE '01000'; +SELECT 'CONTINUE handler 1: 2' AS Msg; +END; +SELECT 'Throw 01000' AS Msg; +SIGNAL SQLSTATE '01000'; +END| + +CALL p13()| +Msg +Throw 01000 +Msg +CONTINUE handler 1: 1 +Msg +CONTINUE handler 2: 1 +Msg +EXIT handler 3 +Msg +CONTINUE handler 1: 2 + +# That's it. Cleanup. + +DROP PROCEDURE p1; +DROP PROCEDURE p2; +DROP PROCEDURE p3; +DROP PROCEDURE p4; +DROP PROCEDURE p5; +DROP PROCEDURE p6; +DROP PROCEDURE p7; +DROP PROCEDURE p8; +DROP PROCEDURE p9; +DROP PROCEDURE p10; +DROP PROCEDURE p11; +DROP PROCEDURE p12; +DROP PROCEDURE p13; + +# Bug#12731619: NESTED SP HANDLERS CAN TRIGGER ASSERTION + +DROP FUNCTION IF EXISTS f1; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(msg VARCHAR(255)); +CREATE FUNCTION f1() RETURNS INT +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 1 +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # handler 2 +BEGIN +INSERT INTO t1 VALUE('WRONG: Inside H2'); +RETURN 2; +END; +INSERT INTO t1 VALUE('CORRECT: Inside H1'); +RETURN 1; +END; +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING # handler 3 +BEGIN +INSERT INTO t1 VALUE('WRONG: Inside H3'); +RETURN 3; +END; +INSERT INTO t1 VALUE('CORRECT: Calling f1()'); +RETURN f1(); # -- exception here +END; +INSERT INTO t1 VALUE('WRONG: Returning 10'); +RETURN 10; +END| + +SELECT f1(); +f1() +1 + +SELECT * FROM t1; +msg +CORRECT: Calling f1() +CORRECT: Inside H1 + +DROP FUNCTION f1; +DROP TABLE t1; + +# Check that handled SQL-conditions are properly cleared from DA. + +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +DROP PROCEDURE IF EXISTS p3; +DROP PROCEDURE IF EXISTS p4; +DROP PROCEDURE IF EXISTS p5; +CREATE TABLE t1(a CHAR, b CHAR, c CHAR); +CREATE TABLE t2(a SMALLINT, b SMALLINT, c SMALLINT); + +# Check that SQL-conditions for which SQL-handler has been invoked, +# are cleared from the Diagnostics Area. Note, there might be several +# SQL-conditions, but SQL-handler must be invoked only once. + +CREATE PROCEDURE p1() +BEGIN +DECLARE EXIT HANDLER FOR SQLWARNING +SELECT 'Warning caught' AS msg; +# The INSERT below raises 3 SQL-conditions (warnings). The EXIT HANDLER +# above must be invoked once (for one condition), but all three conditions +# must be cleared from the Diagnostics Area. +INSERT INTO t1 VALUES('qqqq', 'ww', 'eee'); +# The following INSERT will not be executed, because of the EXIT HANDLER. +INSERT INTO t1 VALUES('zzz', 'xx', 'yyyy'); +END| + +CALL p1()| +msg +Warning caught + +SELECT * FROM t1| +a b c +q w e + +# Check that SQL-conditions for which SQL-handler has *not* been +# invoked, are *still* cleared from the Diagnostics Area. + +CREATE PROCEDURE p2() +BEGIN +DECLARE CONTINUE HANDLER FOR 1292 +SELECT 'Warning 1292 caught' AS msg; +# The following INSERT raises 6 SQL-warnings with code 1292, +# and 3 SQL-warnings with code 1264. The CONTINUE HANDLER above must be +# invoked once, and all nine SQL-warnings must be cleared from +# the Diagnostics Area. +INSERT INTO t2 +SELECT +CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER); +END| + +CALL p2()| +msg +Warning 1292 caught + +# Check that if there are two equally ranked SQL-handlers to handle +# SQL-conditions from SQL-statement, only one of them will be invoked. + +CREATE PROCEDURE p3() +BEGIN +DECLARE CONTINUE HANDLER FOR 1292 +SELECT 'Warning 1292 caught' AS msg; +DECLARE CONTINUE HANDLER FOR 1264 +SELECT 'Warning 1264 caught' AS msg; +# The following INSERT raises 6 SQL-warnings with code 1292, +# and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above +# must be called, and only once. The SQL Standard does not define, which one +# should be invoked. +INSERT INTO t2 +SELECT +CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER); +END| + +CALL p3()| +msg +Warning 1264 caught + +# The same as p3, but 1264 comes first. + +CREATE PROCEDURE p4() +BEGIN +DECLARE CONTINUE HANDLER FOR 1292 +SELECT 'Warning 1292 caught' AS msg; +DECLARE CONTINUE HANDLER FOR 1264 +SELECT 'Warning 1264 caught' AS msg; +# The following INSERT raises 4 SQL-warnings with code 1292, +# and 3 SQL-warnings with code 1264. Only one of the CONTINUE HANDLERs above +# must be called, and only once. The SQL Standard does not define, which one +# should be invoked. +INSERT INTO t2 +SELECT +CAST(999999 AS SIGNED INTEGER), +CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER); +END| + +CALL p4()| +msg +Warning 1264 caught + +# Check that if a SQL-handler raised its own SQL-conditions, there are +# preserved after handler exit. + +CREATE PROCEDURE p5() +BEGIN +DECLARE EXIT HANDLER FOR 1292 +BEGIN +SELECT 'Handler for 1292 (1)' AS Msg; +SIGNAL SQLSTATE '01000' SET MYSQL_ERRNO = 1234; +SHOW WARNINGS; +SELECT 'Handler for 1292 (2)' AS Msg; +END; +INSERT INTO t2 +SELECT +CAST(999999 AS SIGNED INTEGER), +CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER); +END| + +CALL p5()| +Msg +Handler for 1292 (1) +Level Code Message +Warning 1234 Unhandled user-defined warning condition +Msg +Handler for 1292 (2) +Warnings: +Warning 1234 Unhandled user-defined warning condition + +# Check that SQL-conditions are available inside the handler, but +# cleared after the handler exits. + +CREATE PROCEDURE p6() +BEGIN +DECLARE CONTINUE HANDLER FOR 1292 +BEGIN +SHOW WARNINGS; +SELECT 'Handler for 1292' Msg; +END; +INSERT INTO t2 +SELECT +CAST(CONCAT(CAST('1 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('2 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER), +CAST(CONCAT(CAST('3 ' AS UNSIGNED INTEGER), '999999 ') AS SIGNED INTEGER); +END| + +CALL p6()| +Level Code Message +Warning 1292 Truncated incorrect INTEGER value: '1 ' +Warning 1292 Truncated incorrect INTEGER value: '1999999 ' +Warning 1264 Out of range value for column 'a' at row 1 +Warning 1292 Truncated incorrect INTEGER value: '2 ' +Warning 1292 Truncated incorrect INTEGER value: '2999999 ' +Warning 1264 Out of range value for column 'b' at row 1 +Warning 1292 Truncated incorrect INTEGER value: '3 ' +Warning 1292 Truncated incorrect INTEGER value: '3999999 ' +Warning 1264 Out of range value for column 'c' at row 1 +Msg +Handler for 1292 + +DROP PROCEDURE p1; +DROP PROCEDURE p2; +DROP PROCEDURE p3; +DROP PROCEDURE p4; +DROP PROCEDURE p5; +DROP PROCEDURE p6; +DROP TABLE t1; +DROP TABLE t2; + +# Bug#13059316: ASSERTION FAILURE IN SP_RCONTEXT.CC +# Check DECLARE statements that raise conditions before handlers +# are declared. + +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +CREATE PROCEDURE p1() +BEGIN +DECLARE var1 INTEGER DEFAULT 'string'; +DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H1'; +END| + +CALL p1()| +Warnings: +Warning 1366 Incorrect integer value: 'string' for column 'var1' at row 1 + +CREATE PROCEDURE p2() +BEGIN +DECLARE EXIT HANDLER FOR SQLWARNING SELECT 'H2'; +CALL p1(); +END| + +CALL p2()| +H2 +H2 + +DROP PROCEDURE p1; +DROP PROCEDURE p2; +# +# Bug#13113222 RQG_SIGNAL_RESIGNAL FAILED WITH ASSERTION. +# +DROP PROCEDURE IF EXISTS p1; +DROP PROCEDURE IF EXISTS p2; +CREATE PROCEDURE p1() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SELECT 'triggered p1'; +# This will trigger an error. +SIGNAL SQLSTATE 'HY000'; +END| +CREATE PROCEDURE p2() +BEGIN +DECLARE CONTINUE HANDLER FOR SQLWARNING SELECT 'triggered p2'; +# This will trigger a warning. +SIGNAL SQLSTATE '01000'; +END| +SET @old_max_error_count= @@session.max_error_count; +SET SESSION max_error_count= 0; +CALL p1(); +triggered p1 +triggered p1 +CALL p2(); +SET SESSION max_error_count= @old_max_error_count; +DROP PROCEDURE p1; +DROP PROCEDURE p2; + +# Bug#12652873: 61392: Continue handler for NOT FOUND being triggered +# from internal stored function. + +DROP FUNCTION IF EXISTS f1; +DROP FUNCTION IF EXISTS f2; +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 (a INT, b INT); +INSERT INTO t1 VALUES (1, 2); + +# f1() raises NOT_FOUND condition. +# Raising NOT_FOUND can not be simulated by SIGNAL, +# because SIGNAL would raise SQL-error in that case. + +CREATE FUNCTION f1() RETURNS INTEGER +BEGIN +DECLARE v VARCHAR(5) DEFAULT -1; +SELECT b FROM t1 WHERE a = 2 INTO v; +RETURN v; +END| + +# Here we check that the NOT_FOUND condition raised in f1() +# is not visible in the outer function (f2), i.e. the continue +# handler in f2() will not be called. + +CREATE FUNCTION f2() RETURNS INTEGER +BEGIN +DECLARE v INTEGER; +DECLARE CONTINUE HANDLER FOR NOT FOUND +SET @msg = 'Handler activated.'; +SELECT f1() INTO v; +RETURN v; +END| +SET @msg = ''; + +SELECT f2(); +f2() +-1 + +SELECT @msg; +@msg + + +DROP FUNCTION f1; +DROP FUNCTION f2; +DROP TABLE t1; diff --git a/mysql-test/r/sp-prelocking.result b/mysql-test/r/sp-prelocking.result index 186b2c05d34..9e82a966268 100644 --- a/mysql-test/r/sp-prelocking.result +++ b/mysql-test/r/sp-prelocking.result @@ -22,7 +22,7 @@ call sp1(); my-col 1 Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'mysqltest.t1' select database(); database() mysqltest @@ -34,7 +34,7 @@ call mysqltest.sp1(); my-col 1 Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'mysqltest.t1' select database(); database() test diff --git a/mysql-test/r/sp-vars.result b/mysql-test/r/sp-vars.result index a465a29ee4f..0a7ea0d68c5 100644 --- a/mysql-test/r/sp-vars.result +++ b/mysql-test/r/sp-vars.result @@ -400,7 +400,7 @@ Warnings: Note 1305 PROCEDURE test.p2 does not exist DROP TABLE IF EXISTS t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE t1(log_msg VARCHAR(1024)); CREATE PROCEDURE p1(arg VARCHAR(255)) BEGIN diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 6e6f05667ed..912ffe07d10 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -737,8 +737,6 @@ close c; end| insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14)| call cur1()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t1| id data foo 40 @@ -774,8 +772,6 @@ close c1; close c2; end| call cur2()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t3 order by i,s| s i bar 3 @@ -865,8 +861,6 @@ end$ set @@sql_mode = ''| set sql_select_limit = 1| call modes(@c1, @c2)| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed set sql_select_limit = default| select @c1, @c2| @c1 @c2 @@ -1688,64 +1682,42 @@ end| call h_ee()| h_ee Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' call h_es()| h_es -Outer (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' +Inner (bad) call h_en()| h_en -Outer (good) -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed +Inner (bad) call h_ew()| h_ew -Outer (good) +Inner (bad) call h_ex()| h_ex -Outer (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' +Inner (bad) call h_se()| h_se Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' call h_ss()| h_ss Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' call h_sn()| h_sn -Outer (good) -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed +Inner (bad) call h_sw()| h_sw -Outer (good) +Inner (bad) call h_sx()| h_sx -Outer (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' +Inner (bad) call h_ne()| h_ne Inner (good) -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed call h_ns()| h_ns Inner (good) -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed call h_nn()| h_nn Inner (good) -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed call h_we()| h_we Inner (good) @@ -1758,18 +1730,12 @@ Inner (good) call h_xe()| h_xe Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' call h_xs()| h_xs Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' call h_xx()| h_xx Inner (good) -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' drop table t3| drop procedure h_ee| drop procedure h_es| @@ -1918,8 +1884,6 @@ set @x2 = 2; close c1; end| call bug2260()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select @x2| @x2 2 @@ -2063,8 +2027,6 @@ insert into t3 values (123456789012); insert into t3 values (0); end| call bug2780()| -Warnings: -Warning 1264 Out of range value for column 's1' at row 1 select @x| @x 1 @@ -2487,8 +2449,6 @@ declare continue handler for sqlstate 'HY000' begin end; select s1 from t3 union select s2 from t3; end| call bug4904()| -Warnings: -Error 1267 Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (latin2_general_ci,IMPLICIT) for operation 'UNION' drop procedure bug4904| drop table t3| drop procedure if exists bug336| @@ -2628,17 +2588,13 @@ select row_count()| row_count() 1 call bug4905()| -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' select row_count()| row_count() --1 +0 call bug4905()| -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' select row_count()| row_count() --1 +0 select * from t3| s1 1 @@ -2659,14 +2615,10 @@ insert into t3 values (1)| call bug6029()| sqlstate 23000 sqlstate 23000 -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' delete from t3| call bug6029()| 1136 1136 -Warnings: -Error 1136 Column count doesn't match value count at row 1 drop procedure bug6029| drop table t3| drop procedure if exists bug8540| @@ -2961,23 +2913,15 @@ end| call bug6900()| 2 2 -Warnings: -Error 1136 Column count doesn't match value count at row 1 call bug9074()| x1 x2 x3 x4 x5 x6 1 1 1 1 1 1 -Warnings: -Error 1062 Duplicate entry 'a' for key 'w' call bug6900_9074(0)| sqlexception sqlexception -Warnings: -Error 1136 Column count doesn't match value count at row 1 call bug6900_9074(1)| -23000 -23000 -Warnings: -Error 1062 Duplicate entry 'a' for key 'w' +sqlexception +sqlexception drop procedure bug6900| drop procedure bug9074| drop procedure bug6900_9074| @@ -3020,13 +2964,9 @@ delete from t1| call bug9856()| 16 16 -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed call bug9856()| 16 16 -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed drop procedure bug9856| drop procedure if exists bug9674_1| drop procedure if exists bug9674_2| @@ -3256,8 +3196,6 @@ x 2 x 3 -Warnings: -Error 1326 Cursor is not open call bug10961()| x 1 @@ -3265,8 +3203,6 @@ x 2 x 3 -Warnings: -Error 1326 Cursor is not open drop procedure bug10961| DROP PROCEDURE IF EXISTS bug6866| DROP VIEW IF EXISTS tv| @@ -3274,9 +3210,9 @@ Warnings: Note 1051 Unknown table 'test.tv' DROP TABLE IF EXISTS tt1,tt2,tt3| Warnings: -Note 1051 Unknown table 'tt1' -Note 1051 Unknown table 'tt2' -Note 1051 Unknown table 'tt3' +Note 1051 Unknown table 'test.tt1' +Note 1051 Unknown table 'test.tt2' +Note 1051 Unknown table 'test.tt3' CREATE TABLE tt1 (a1 int, a2 int, a3 int, data varchar(10))| CREATE TABLE tt2 (a2 int, data2 varchar(10))| CREATE TABLE tt3 (a3 int, data3 varchar(10))| @@ -3382,11 +3318,7 @@ insert into t1 values ('Name4', 13), ('Name5', 14)| call bug11529()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed call bug11529()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed delete from t1| drop procedure bug11529| set character set utf8| @@ -3560,32 +3492,24 @@ end; end if; end| call bug12168('a')| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t4| a 1 3 truncate t4| call bug12168('b')| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t4| a 2 4 truncate t4| call bug12168('a')| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t4| a 1 3 truncate t4| call bug12168('b')| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select * from t4| a 2 @@ -3885,8 +3809,6 @@ end| call bug7049_2()| Result Caught it -Warnings: -Error 1062 Duplicate entry '42' for key 'x' select * from t3| x 42 @@ -3894,16 +3816,12 @@ delete from t3| call bug7049_4()| Result Caught it -Warnings: -Error 1062 Duplicate entry '42' for key 'x' select * from t3| x 42 select bug7049_2()| bug7049_2() 1 -Warnings: -Error 1062 Duplicate entry '42' for key 'x' drop table t3| drop procedure bug7049_1| drop procedure bug7049_2| @@ -4031,8 +3949,6 @@ end| call bug14845()| a 0 -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed drop procedure bug14845| drop procedure if exists bug13549_1| drop procedure if exists bug13549_2| @@ -4236,8 +4152,6 @@ end| call bug13729()| 55 55 -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' select * from t3| s1 1 @@ -4274,15 +4188,11 @@ Handler boo v isnull(v) NULL 1 -Warnings: -Error 1054 Unknown column 'undefined_var' in 'field list' call bug14643_2()| Handler boo Handler boo -Warnings: -Error 1054 Unknown column 'undefined_var' in 'field list' drop procedure bug14643_1| drop procedure bug14643_2| drop procedure if exists bug14304| @@ -4606,15 +4516,11 @@ Handler error End done -Warnings: -Error 1054 Unknown column 'v' in 'field list' call bug14498_2()| Handler error End done -Warnings: -Error 1054 Unknown column 'v' in 'field list' call bug14498_3()| v maybe @@ -4622,22 +4528,16 @@ Handler error End done -Warnings: -Error 1054 Unknown column 'v' in 'field list' call bug14498_4()| Handler error End done -Warnings: -Error 1054 Unknown column 'v' in 'field list' call bug14498_5()| Handler error End done -Warnings: -Error 1054 Unknown column 'v' in 'field list' drop procedure bug14498_1| drop procedure bug14498_2| drop procedure bug14498_3| @@ -4702,8 +4602,6 @@ Before NOT FOUND condition is triggered After NOT FOUND condtition is triggered xid xdone 1 1 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed call bug15231_3()| Result Missed it (correct) @@ -4711,8 +4609,6 @@ Level Code Message Warning 1366 Incorrect decimal value: 'zap' for column 'x' at row 1 Result Caught it (correct) -Warnings: -Warning 1366 Incorrect decimal value: 'zap' for column 'x' at row 1 call bug15231_5()| Result Missed it (correct) @@ -4741,8 +4637,6 @@ end| call bug15011()| Handler Inner -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' drop procedure bug15011| drop table t3| drop procedure if exists bug17476| @@ -4818,8 +4712,6 @@ i 1 i 0 -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed drop table t3| drop procedure bug16887| drop procedure if exists bug16474_1| @@ -4892,8 +4784,6 @@ declare continue handler for sqlexception begin end; select no_such_function(); end| call bug18787()| -Warnings: -Error 1305 FUNCTION test.no_such_function does not exist drop procedure bug18787| create database bug18344_012345678901| use bug18344_012345678901| @@ -5267,8 +5157,6 @@ statement failed statement failed statement after update statement after update -Warnings: -Error 1242 Subquery returns more than 1 row select * from t3| a 1 @@ -5280,8 +5168,6 @@ statement failed statement failed statement after update statement after update -Warnings: -Error 1242 Subquery returns more than 1 row select * from t3| a 1 @@ -5314,8 +5200,6 @@ in continue handler in continue handler reachable code a2 reachable code a2 -Warnings: -Error 1242 Subquery returns more than 1 row select * from t3| a 1 @@ -5331,8 +5215,6 @@ in continue handler in continue handler reachable code a2 reachable code a2 -Warnings: -Error 1242 Subquery returns more than 1 row select * from t3| a 1 @@ -5366,8 +5248,6 @@ in continue handler in continue handler reachable code a2 reachable code a2 -Warnings: -Error 1305 FUNCTION test.no_such_function does not exist drop procedure bug8153_proc_a| drop procedure bug8153_proc_b| drop table t3| @@ -5547,13 +5427,9 @@ end| select func_20028_a()| func_20028_a() 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select func_20028_b()| func_20028_b() 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select func_20028_c()| ERROR 22012: Division by 0 call proc_20028_a()| @@ -5606,13 +5482,9 @@ end| select func_20028_a()| func_20028_a() 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select func_20028_b()| func_20028_b() 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select func_20028_c()| func_20028_c() NULL @@ -5936,13 +5808,9 @@ end| select func_8407_a()| func_8407_a() NULL -Warnings: -Error 1146 Table 'test.no_such_view' doesn't exist select func_8407_b()| func_8407_b() 1500 -Warnings: -Error 1146 Table 'test.no_such_view' doesn't exist drop function func_8407_a| drop function func_8407_b| drop table if exists table_26503| @@ -6064,8 +5932,6 @@ looping i looping 0 leaving handler leaving handler -Warnings: -Error 1062 Duplicate entry '1' for key 'a' call proc_26503_ok_2(2)| do something do something @@ -6077,8 +5943,6 @@ looping i looping 4 leaving handler leaving handler -Warnings: -Error 1062 Duplicate entry '2' for key 'a' call proc_26503_ok_3(3)| do something do something @@ -6098,8 +5962,6 @@ looping i looping 0 leaving handler leaving handler -Warnings: -Error 1062 Duplicate entry '3' for key 'a' call proc_26503_ok_4(4)| do something do something @@ -6111,8 +5973,6 @@ looping i looping 4 leaving handler leaving handler -Warnings: -Error 1062 Duplicate entry '4' for key 'a' drop table table_26503| drop procedure proc_26503_ok_1| drop procedure proc_26503_ok_2| @@ -6244,8 +6104,6 @@ END| SELECT bug5274_f2()| bug5274_f2() x -Warnings: -Warning 1265 Data truncated for column 'bug5274_f1' at row 1 DROP FUNCTION bug5274_f1| DROP FUNCTION bug5274_f2| drop procedure if exists proc_21513| @@ -6338,19 +6196,13 @@ c1 SELECT f1(2); f1(2) 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed PREPARE s1 FROM 'SELECT f1(2)'; EXECUTE s1; f1(2) 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed EXECUTE s1; f1(2) 0 -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed DROP PROCEDURE p1; DROP PROCEDURE p2; DROP FUNCTION f1; @@ -6826,8 +6678,6 @@ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET @exception:= 'run'; SELECT x FROM t1; END| CALL bug29770(); -Warnings: -Error 1054 Unknown column 'x' in 'field list' SELECT @state, @exception; @state @exception run NULL @@ -6866,8 +6716,6 @@ end; end while; end// call proc_33618(20); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed drop table t_33618; drop procedure proc_33618; # @@ -7803,9 +7651,6 @@ END $ SELECT f1(); f1() 1 -Warnings: -Error 1424 Recursive stored functions and triggers are not allowed. -Error 1305 FUNCTION test.f1 does not exist DROP FUNCTION f1; # ------------------------------------------------------------------ # -- End of 5.1 tests @@ -7943,8 +7788,6 @@ message You should see this message and the warning that generated this Level Code Message Warning 1329 No data - zero rows fetched, selected, or processed -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed drop procedure p1; drop procedure p0; drop table t1; diff --git a/mysql-test/r/sp_trans.result b/mysql-test/r/sp_trans.result index b91dc898f12..5526fc19aae 100644 --- a/mysql-test/r/sp_trans.result +++ b/mysql-test/r/sp_trans.result @@ -99,8 +99,6 @@ return i; end| set @error_in_func:= 0| insert into t1 values (bug10015_6(5)), (bug10015_6(6))| -Warnings: -Error 1062 Duplicate entry '1' for key 'PRIMARY' select @error_in_func| @error_in_func 1 @@ -526,8 +524,6 @@ until done end repeat; close c; end| call bug14210()| -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed select count(*) from t4| count(*) 256 diff --git a/mysql-test/r/statistics.result b/mysql-test/r/statistics.result index ad12c60cce5..e7d25f3476c 100644 --- a/mysql-test/r/statistics.result +++ b/mysql-test/r/statistics.result @@ -1113,9 +1113,19 @@ test t2 idx4 4 1.0000 ALTER TABLE t2 CHANGE COLUMN b b varchar(32); SELECT * FROM mysql.index_stats ORDER BY index_name, prefix_arity, table_name; db_name table_name index_name prefix_arity avg_frequency +test t2 PRIMARY 1 1.0000 +test t2 PRIMARY 2 1.0000 test t2 idx2 1 7.0000 test t2 idx2 2 2.3846 +test t2 idx2 3 1.0000 +test t2 idx2 4 1.0000 test t2 idx3 1 8.5000 +test t2 idx3 2 1.0000 +test t2 idx3 3 1.0000 +test t2 idx4 1 6.2000 +test t2 idx4 2 1.7222 +test t2 idx4 3 1.1154 +test t2 idx4 4 1.0000 ANALYZE TABLE t2 PERSISTENT FOR COLUMNS ALL INDEXES ALL; Table Op Msg_type Msg_text test.t2 analyze status OK @@ -1172,13 +1182,13 @@ test t2 f 1 5 0.2000 1.0000 6.4000 0 NULL NULL SELECT * FROM mysql.index_stats; db_name table_name index_name prefix_arity avg_frequency test t2 idx3 1 8.5000 -test t2 idx3 2 1.0000 -test t2 idx2 3 1.0000 test t2 idx2 1 7.0000 test t2 idx2 2 2.3846 -test t2 idx4 3 1.0000 +test t2 idx2 3 1.0000 test t2 idx4 1 6.2000 test t2 idx4 2 2.2308 +test t2 idx4 3 1.0000 +test t2 idx3 2 1.0000 test t2 PRIMARY 1 1.0000 ANALYZE TABLE t1; Table Op Msg_type Msg_text @@ -1198,17 +1208,17 @@ test t1 f 1 5 0.2000 1.0000 6.4000 0 NULL NULL test t1 b NULL NULL 0.2000 17.1250 NULL NULL NULL NULL SELECT * FROM mysql.index_stats; db_name table_name index_name prefix_arity avg_frequency -test t2 idx3 1 8.5000 -test t2 idx3 2 1.0000 -test t2 idx2 3 1.0000 -test t2 idx2 1 7.0000 -test t2 idx2 2 2.3846 test t1 idx2 1 7.0000 +test t2 idx3 1 8.5000 test t1 idx3 1 8.5000 test t1 PRIMARY 1 1.0000 -test t2 idx4 3 1.0000 +test t2 idx2 1 7.0000 +test t2 idx2 2 2.3846 +test t2 idx2 3 1.0000 test t2 idx4 1 6.2000 test t2 idx4 2 2.2308 +test t2 idx4 3 1.0000 +test t2 idx3 2 1.0000 test t2 PRIMARY 1 1.0000 test t1 idx2 2 2.3846 test t1 idx1 1 NULL diff --git a/mysql-test/r/strict.result b/mysql-test/r/strict.result index cee4cf3ebe6..7cc29ae4ed6 100644 --- a/mysql-test/r/strict.result +++ b/mysql-test/r/strict.result @@ -1190,8 +1190,6 @@ select'a'; insert into t1 values (200); end;| call t1(); a a -Warnings: -Error 1264 Out of range value for column 'col1' at row 1 select * from t1; col1 drop procedure t1; @@ -1501,3 +1499,29 @@ count(*) 0 drop table t1; End of 5.0 tests +# +# Start of 5.6 tests +# +# +# WL#946 TIME/TIMESTAMP/DATETIME with fractional seconds: CAST to DATETIME +# +# +# STR_TO_DATE with NO_ZERO_DATE did not return NULL (with warning) +# in get_date(). Only did in val_str() and val_int(). +SET sql_mode='NO_ZERO_DATE'; +SELECT STR_TO_DATE('2001','%Y'),CONCAT(STR_TO_DATE('2001','%Y')), STR_TO_DATE('2001','%Y')+1, STR_TO_DATE('0','%Y')+1, STR_TO_DATE('0000','%Y')+1; +STR_TO_DATE('2001','%Y') CONCAT(STR_TO_DATE('2001','%Y')) STR_TO_DATE('2001','%Y')+1 STR_TO_DATE('0','%Y')+1 STR_TO_DATE('0000','%Y')+1 +2001-00-00 2001-00-00 20010001 20000001 NULL +Warnings: +Warning 1411 Incorrect datetime value: '0000' for function str_to_date +SET sql_mode='NO_ZERO_IN_DATE'; +SELECT STR_TO_DATE('2001','%Y'),CONCAT(STR_TO_DATE('2001','%Y')), STR_TO_DATE('2001','%Y')+1, STR_TO_DATE('0000','%Y')+1; +STR_TO_DATE('2001','%Y') CONCAT(STR_TO_DATE('2001','%Y')) STR_TO_DATE('2001','%Y')+1 STR_TO_DATE('0000','%Y')+1 +NULL NULL NULL 1 +Warnings: +Warning 1411 Incorrect datetime value: '2001' for function str_to_date +Warning 1411 Incorrect datetime value: '2001' for function str_to_date +Warning 1411 Incorrect datetime value: '2001' for function str_to_date +# +# End of 5.6 tests +# diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index eac53365329..824079c3a59 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -5898,7 +5898,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/subselect2.result b/mysql-test/r/subselect2.result index 4fd303dfd44..38e955e349c 100644 --- a/mysql-test/r/subselect2.result +++ b/mysql-test/r/subselect2.result @@ -184,7 +184,7 @@ DROP TABLE t1,t2,t3; # DROP TABLE IF EXISTS `t1`; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' CREATE TABLE `t1` ( `node_uid` bigint(20) unsigned DEFAULT NULL, `date` datetime DEFAULT NULL, diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index ff768886434..df7733a1cd3 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -2174,7 +2174,7 @@ FROM t1 AS alias1 ) IS NOT NULL; ERROR 21000: Subquery returns more than 1 row DROP TABLE t2; -ERROR 42S02: Unknown table 't2' +ERROR 42S02: Unknown table 'test.t2' DROP TABLE t1; # # LP BUG#1000649 EXPLAIN shows incorrectly a non-correlated constant IN subquery is correlated diff --git a/mysql-test/r/subselect_exists_to_in.result b/mysql-test/r/subselect_exists_to_in.result index a71e57b2566..19ae87f473f 100644 --- a/mysql-test/r/subselect_exists_to_in.result +++ b/mysql-test/r/subselect_exists_to_in.result @@ -5906,7 +5906,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/subselect_innodb.result b/mysql-test/r/subselect_innodb.result index a5800883711..e9e1ccd0bf6 100644 --- a/mysql-test/r/subselect_innodb.result +++ b/mysql-test/r/subselect_innodb.result @@ -437,8 +437,8 @@ drop table t1; # drop table if exists `t1`,`t2`; Warnings: -Note 1051 Unknown table 't1' -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t1' +Note 1051 Unknown table 'test.t2' create table `t1`(`a` char(1) character set utf8)engine=innodb; create table `t2`(`b` char(1) character set utf8)engine=memory; select distinct (select 1 from `t2` where `a`) `d2` from `t1`; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index bf98b912a9e..627e5b03e32 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -5899,7 +5899,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index 05f7a25e0ef..5368198f77a 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -5895,7 +5895,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index ee84bfd1eca..ad0d2ffe6a6 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -5904,7 +5904,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index 5a7e303f4b9..39cc060e955 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -5895,7 +5895,7 @@ CREATE TABLE t2 ( f3 int, f10 int, KEY (f10,f3)) ; INSERT IGNORE INTO t2 VALUES (NULL,NULL),(5,0); DROP TABLE IF EXISTS t3; Warnings: -Note 1051 Unknown table 't3' +Note 1051 Unknown table 'test.t3' CREATE TABLE t3 ( f3 int) ; INSERT INTO t3 VALUES (0),(0); SELECT a1.f3 AS r FROM t2 AS a1 , t1 WHERE a1.f3 < ALL ( SELECT f3 FROM t3 WHERE f3 = 1 ) ; diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 08c17b1afd5..eda11d95b22 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -15,15 +15,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -131,6 +127,7 @@ user CREATE TABLE `user` ( `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, + `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; @@ -253,7 +250,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -271,7 +268,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' show create table table_stats; Table Create Table diff --git a/mysql-test/r/system_mysql_db_fix40123.result b/mysql-test/r/system_mysql_db_fix40123.result index 2f76ee654c8..71e94a7432d 100644 --- a/mysql-test/r/system_mysql_db_fix40123.result +++ b/mysql-test/r/system_mysql_db_fix40123.result @@ -15,15 +15,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -131,6 +127,7 @@ user CREATE TABLE `user` ( `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, + `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; @@ -253,7 +250,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -271,7 +268,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' show create table table_stats; Table Create Table diff --git a/mysql-test/r/system_mysql_db_fix50030.result b/mysql-test/r/system_mysql_db_fix50030.result index 2f76ee654c8..71e94a7432d 100644 --- a/mysql-test/r/system_mysql_db_fix50030.result +++ b/mysql-test/r/system_mysql_db_fix50030.result @@ -15,15 +15,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -131,6 +127,7 @@ user CREATE TABLE `user` ( `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, + `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; @@ -253,7 +250,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -271,7 +268,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' show create table table_stats; Table Create Table diff --git a/mysql-test/r/system_mysql_db_fix50117.result b/mysql-test/r/system_mysql_db_fix50117.result index 2f76ee654c8..71e94a7432d 100644 --- a/mysql-test/r/system_mysql_db_fix50117.result +++ b/mysql-test/r/system_mysql_db_fix50117.result @@ -15,15 +15,11 @@ host index_stats innodb_index_stats innodb_table_stats -ndb_binlog_index plugin proc procs_priv proxies_priv servers -slave_master_info -slave_relay_log_info -slave_worker_info slow_log table_stats tables_priv @@ -131,6 +127,7 @@ user CREATE TABLE `user` ( `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, + `password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; @@ -253,7 +250,7 @@ Table Create Table general_log CREATE TABLE `general_log` ( `event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), `user_host` mediumtext NOT NULL, - `thread_id` int(11) NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL, `server_id` int(10) unsigned NOT NULL, `command_type` varchar(64) NOT NULL, `argument` mediumtext NOT NULL @@ -271,7 +268,8 @@ slow_log CREATE TABLE `slow_log` ( `last_insert_id` int(11) NOT NULL, `insert_id` int(11) NOT NULL, `server_id` int(10) unsigned NOT NULL, - `sql_text` mediumtext NOT NULL + `sql_text` mediumtext NOT NULL, + `thread_id` bigint(21) unsigned NOT NULL ) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='Slow log' show create table table_stats; Table Create Table diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result index 33f5c6b5165..a5182a03e63 100644 --- a/mysql-test/r/temp_table.result +++ b/mysql-test/r/temp_table.result @@ -1,5 +1,25 @@ drop table if exists t1,t2; drop view if exists v1; +# +# test basic creation of temporary tables together with normal table +# +create table t1 (a int); +create temporary table t1 AS SELECT 1; +create temporary table t1 AS SELECT 1; +ERROR 42S01: Table 't1' already exists +create temporary table t1 (a int); +ERROR 42S01: Table 't1' already exists +drop temporary table t1; +drop table t1; +create temporary table t1 AS SELECT 1; +create temporary table t1 AS SELECT 1; +ERROR 42S01: Table 't1' already exists +create temporary table t1 (a int); +ERROR 42S01: Table 't1' already exists +drop temporary table t1; +# +# Test with rename +# CREATE TABLE t1 (c int not null, d char (10) not null); insert into t1 values(1,""),(2,"a"),(3,"b"); CREATE TEMPORARY TABLE t1 (a int not null, b char (10) not null); @@ -145,7 +165,7 @@ DROP TABLE t1; CREATE TABLE t1 (i INT); CREATE TEMPORARY TABLE t2 (i INT); DROP TEMPORARY TABLE t2, t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' SELECT * FROM t2; ERROR 42S02: Table 'test.t2' doesn't exist SELECT * FROM t1; diff --git a/mysql-test/r/truncate_coverage.result b/mysql-test/r/truncate_coverage.result index 728702f7ab5..395c71b2e6b 100644 --- a/mysql-test/r/truncate_coverage.result +++ b/mysql-test/r/truncate_coverage.result @@ -11,7 +11,7 @@ HANDLER t1 OPEN; # # connection default LOCK TABLE t1 WRITE; -SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +SET DEBUG_SYNC='mdl_upgrade_lock SIGNAL waiting'; TRUNCATE TABLE t1; # # connection con2 @@ -37,7 +37,7 @@ HANDLER t1 OPEN; # # connection default LOCK TABLE t1 WRITE; -SET DEBUG_SYNC='mdl_upgrade_shared_lock_to_exclusive SIGNAL waiting'; +SET DEBUG_SYNC='mdl_upgrade_lock SIGNAL waiting'; TRUNCATE TABLE t1; # # connection con2 @@ -50,7 +50,7 @@ HANDLER t1 CLOSE; ERROR 42S02: Table 'test.t1' doesn't exist UNLOCK TABLES; DROP TABLE t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' SET DEBUG_SYNC='RESET'; CREATE TABLE t1 (c1 INT); INSERT INTO t1 VALUES (1); diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 6b6b5a2392c..5b3594fe503 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -680,7 +680,7 @@ select 0.8 = 0.7 + 0.1; 1 drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' create table t1 (col1 decimal(38)); insert into t1 values (12345678901234567890123456789012345678); select * from t1; diff --git a/mysql-test/r/upgrade.result b/mysql-test/r/upgrade.result index d565bb2dbd6..d9252791c0a 100644 --- a/mysql-test/r/upgrade.result +++ b/mysql-test/r/upgrade.result @@ -50,6 +50,10 @@ show tables; Tables_in_test txu#p#p1 txu@0023p@0023p1 +insert into `txu@0023p@0023p1` values (2); +select * from `txu@0023p@0023p1`; +s1 +2 select * from `txu#p#p1`; s1 1 diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 40d6c71b5af..98b4b77e425 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -205,7 +205,7 @@ ERROR 42S02: Unknown table 'v100' drop view t1; ERROR HY000: 'test.t1' is not VIEW drop table v1; -ERROR 42S02: Unknown table 'v1' +ERROR 42S02: Unknown table 'test.v1' drop view v1,v2; drop table t1; create table t1 (a int); @@ -3951,8 +3951,6 @@ create view a as select 1; end| call p(); call p(); -Warnings: -Error 1050 Table 'a' already exists drop view a; drop procedure p; # diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index bfd09bfa9cd..2078bc6d63f 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -357,13 +357,9 @@ use mysqltest; select * from v1; f2() NULL -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select * from v2; f2() NULL -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select * from v3; ERROR HY000: View 'mysqltest.v3' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them select * from v4; @@ -403,13 +399,9 @@ ERROR HY000: View 'mysqltest.v2' references invalid table(s) or column(s) or fun select * from v3; f2() NULL -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select * from v4; f2() NULL -Warnings: -Warning 1329 No data - zero rows fetched, selected, or processed select * from v5; ERROR HY000: View 'mysqltest.v5' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1, v2, v3, v4, v5; diff --git a/mysql-test/r/warnings.result b/mysql-test/r/warnings.result index e033b358b6b..b4b345ca260 100644 --- a/mysql-test/r/warnings.result +++ b/mysql-test/r/warnings.result @@ -48,13 +48,13 @@ drop table t1; set SQL_WARNINGS=0; drop temporary table if exists not_exists; Warnings: -Note 1051 Unknown table 'not_exists' +Note 1051 Unknown table 'test.not_exists' drop table if exists not_exists_table; Warnings: -Note 1051 Unknown table 'not_exists_table' +Note 1051 Unknown table 'test.not_exists_table' show warnings limit 1; Level Code Message -Note 1051 Unknown table 'not_exists_table' +Note 1051 Unknown table 'test.not_exists_table' drop database if exists not_exists_db; Warnings: Note 1008 Can't drop database 'not_exists_db'; database doesn't exist @@ -311,10 +311,10 @@ insert into t2 values(@q); ERROR 22001: Data too long for column 'c_tinyblob' at row 1 drop table t1, t2; DROP TABLE t1; -ERROR 42S02: Unknown table 't1' +ERROR 42S02: Unknown table 'test.t1' SHOW ERRORS; Level Code Message -Error 1051 Unknown table 't1' +Error 1051 Unknown table 'test.t1' End of 5.0 tests set sql_mode = default; select CAST(a AS DECIMAL(13,5)) FROM (SELECT '' as a) t; diff --git a/mysql-test/std_data/bug48633.ARM b/mysql-test/std_data/bug48633.ARM new file mode 100644 index 00000000000..5a46ddacc3a Binary files /dev/null and b/mysql-test/std_data/bug48633.ARM differ diff --git a/mysql-test/std_data/bug48633.ARZ b/mysql-test/std_data/bug48633.ARZ new file mode 100644 index 00000000000..0aa85c690e5 Binary files /dev/null and b/mysql-test/std_data/bug48633.ARZ differ diff --git a/mysql-test/std_data/bug48633.frm b/mysql-test/std_data/bug48633.frm new file mode 100644 index 00000000000..5c2a8a688b7 Binary files /dev/null and b/mysql-test/std_data/bug48633.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/README b/mysql-test/std_data/mysql5613mysql/README new file mode 100644 index 00000000000..44be93c231e --- /dev/null +++ b/mysql-test/std_data/mysql5613mysql/README @@ -0,0 +1,2 @@ + +Tables created by mysql_install_db from MySQL-5.6.13 diff --git a/mysql-test/std_data/mysql5613mysql/columns_priv.MYD b/mysql-test/std_data/mysql5613mysql/columns_priv.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/columns_priv.MYI b/mysql-test/std_data/mysql5613mysql/columns_priv.MYI new file mode 100644 index 00000000000..59cc88e9c2e Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/columns_priv.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/columns_priv.frm b/mysql-test/std_data/mysql5613mysql/columns_priv.frm new file mode 100644 index 00000000000..061c20d8cfd Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/columns_priv.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/db.MYD b/mysql-test/std_data/mysql5613mysql/db.MYD new file mode 100644 index 00000000000..75caf717074 --- /dev/null +++ b/mysql-test/std_data/mysql5613mysql/db.MYD @@ -0,0 +1 @@ +ÿ% test ÿ% test\_%  \ No newline at end of file diff --git a/mysql-test/std_data/mysql5613mysql/db.MYI b/mysql-test/std_data/mysql5613mysql/db.MYI new file mode 100644 index 00000000000..9df866be2bf Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/db.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/db.frm b/mysql-test/std_data/mysql5613mysql/db.frm new file mode 100644 index 00000000000..81f810253b9 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/db.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/event.MYD b/mysql-test/std_data/mysql5613mysql/event.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/event.MYI b/mysql-test/std_data/mysql5613mysql/event.MYI new file mode 100644 index 00000000000..9be2dfaeb5c Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/event.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/event.frm b/mysql-test/std_data/mysql5613mysql/event.frm new file mode 100644 index 00000000000..870ccb6f579 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/event.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/func.MYD b/mysql-test/std_data/mysql5613mysql/func.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/func.MYI b/mysql-test/std_data/mysql5613mysql/func.MYI new file mode 100644 index 00000000000..f56c6da600a Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/func.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/func.frm b/mysql-test/std_data/mysql5613mysql/func.frm new file mode 100644 index 00000000000..bb64f83542b Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/func.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/plugin.MYD b/mysql-test/std_data/mysql5613mysql/plugin.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/plugin.MYI b/mysql-test/std_data/mysql5613mysql/plugin.MYI new file mode 100644 index 00000000000..fd8a17b5442 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/plugin.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/plugin.frm b/mysql-test/std_data/mysql5613mysql/plugin.frm new file mode 100644 index 00000000000..f64a5517c53 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/plugin.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/proc.MYD b/mysql-test/std_data/mysql5613mysql/proc.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/proc.MYI b/mysql-test/std_data/mysql5613mysql/proc.MYI new file mode 100644 index 00000000000..7c6682bcbc7 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/proc.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/proc.frm b/mysql-test/std_data/mysql5613mysql/proc.frm new file mode 100644 index 00000000000..1b9f5a83d81 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/proc.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/procs_priv.MYD b/mysql-test/std_data/mysql5613mysql/procs_priv.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/procs_priv.MYI b/mysql-test/std_data/mysql5613mysql/procs_priv.MYI new file mode 100644 index 00000000000..e20e1d5af91 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/procs_priv.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/procs_priv.frm b/mysql-test/std_data/mysql5613mysql/procs_priv.frm new file mode 100644 index 00000000000..8fd024887d2 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/procs_priv.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/proxies_priv.MYD b/mysql-test/std_data/mysql5613mysql/proxies_priv.MYD new file mode 100644 index 00000000000..c645be421b2 --- /dev/null +++ b/mysql-test/std_data/mysql5613mysql/proxies_priv.MYD @@ -0,0 +1 @@ +ÿlocalhost root  Rà‹ÿnoter root  Rà‹ \ No newline at end of file diff --git a/mysql-test/std_data/mysql5613mysql/proxies_priv.MYI b/mysql-test/std_data/mysql5613mysql/proxies_priv.MYI new file mode 100644 index 00000000000..875f80da139 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/proxies_priv.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/proxies_priv.frm b/mysql-test/std_data/mysql5613mysql/proxies_priv.frm new file mode 100644 index 00000000000..4640bab0ef6 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/proxies_priv.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/servers.MYD b/mysql-test/std_data/mysql5613mysql/servers.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/servers.MYI b/mysql-test/std_data/mysql5613mysql/servers.MYI new file mode 100644 index 00000000000..22aa14ba1e7 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/servers.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/servers.frm b/mysql-test/std_data/mysql5613mysql/servers.frm new file mode 100644 index 00000000000..3ae10fedd8a Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/servers.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/tables_priv.MYD b/mysql-test/std_data/mysql5613mysql/tables_priv.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/tables_priv.MYI b/mysql-test/std_data/mysql5613mysql/tables_priv.MYI new file mode 100644 index 00000000000..678910275c8 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/tables_priv.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/tables_priv.frm b/mysql-test/std_data/mysql5613mysql/tables_priv.frm new file mode 100644 index 00000000000..64684d41296 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/tables_priv.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone.MYD b/mysql-test/std_data/mysql5613mysql/time_zone.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/time_zone.MYI b/mysql-test/std_data/mysql5613mysql/time_zone.MYI new file mode 100644 index 00000000000..25955801d00 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone.frm b/mysql-test/std_data/mysql5613mysql/time_zone.frm new file mode 100644 index 00000000000..f62d93408a4 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.MYD b/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.MYI b/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.MYI new file mode 100644 index 00000000000..334ad10e304 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.frm b/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.frm new file mode 100644 index 00000000000..a2e7f133b6d Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_leap_second.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_name.MYD b/mysql-test/std_data/mysql5613mysql/time_zone_name.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_name.MYI b/mysql-test/std_data/mysql5613mysql/time_zone_name.MYI new file mode 100644 index 00000000000..ca893298f47 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_name.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_name.frm b/mysql-test/std_data/mysql5613mysql/time_zone_name.frm new file mode 100644 index 00000000000..7f3ac662b9b Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_name.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition.MYD b/mysql-test/std_data/mysql5613mysql/time_zone_transition.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition.MYI b/mysql-test/std_data/mysql5613mysql/time_zone_transition.MYI new file mode 100644 index 00000000000..a5953d502d7 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_transition.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition.frm b/mysql-test/std_data/mysql5613mysql/time_zone_transition.frm new file mode 100644 index 00000000000..97a72a2c34a Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_transition.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.MYD b/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.MYD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.MYI b/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.MYI new file mode 100644 index 00000000000..17a966fdab6 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.frm b/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.frm new file mode 100644 index 00000000000..2c392e786b7 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/time_zone_transition_type.frm differ diff --git a/mysql-test/std_data/mysql5613mysql/user.MYD b/mysql-test/std_data/mysql5613mysql/user.MYD new file mode 100644 index 00000000000..78a2112b37b Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/user.MYD differ diff --git a/mysql-test/std_data/mysql5613mysql/user.MYI b/mysql-test/std_data/mysql5613mysql/user.MYI new file mode 100644 index 00000000000..c4746cb7a02 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/user.MYI differ diff --git a/mysql-test/std_data/mysql5613mysql/user.frm b/mysql-test/std_data/mysql5613mysql/user.frm new file mode 100644 index 00000000000..de49c945b31 Binary files /dev/null and b/mysql-test/std_data/mysql5613mysql/user.frm differ diff --git a/mysql-test/suite/archive/archive.result b/mysql-test/suite/archive/archive.result index a491b5bb9ee..b92018fba8b 100644 --- a/mysql-test/suite/archive/archive.result +++ b/mysql-test/suite/archive/archive.result @@ -12729,16 +12729,6 @@ id id name name 1 1 a b 2 2 a b DROP TABLE t1,t2; -flush tables; -SHOW CREATE TABLE t1; -ERROR HY000: Table upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it! -SELECT * FROM t1; -ERROR HY000: Table upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it! -INSERT INTO t1 (col1, col2) VALUES (1, "value"); -ERROR HY000: Table upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it! -REPAIR TABLE t1; -Table Op Msg_type Msg_text -test.t1 repair status OK SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( @@ -12747,6 +12737,10 @@ t1 CREATE TABLE `t1` ( ) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 SELECT * FROM t1; col1 col2 +INSERT INTO t1 (col1, col2) VALUES (1, "value"); +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK DROP TABLE t1; # # Ensure that TRUNCATE fails for non-empty archive tables. @@ -12792,6 +12786,62 @@ a b c d e f DROP TABLE t1; SET sort_buffer_size=DEFAULT; # +# BUG#11756687 - 48633: ARCHIVE TABLES ARE NOT UPGRADEABLE +# +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` text, + `c` varchar(255) DEFAULT NULL, + `d` blob, + `e` blob +) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +FLUSH TABLE t1; +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check error Table upgrade required. Please do "REPAIR TABLE `t1`" or dump/reload to fix it! +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +INSERT INTO t1 VALUES(3, 'text', 'varchar', 'blob1', 'blob2'); +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +3 text varchar blob1 blob2 +FLUSH TABLE t1; +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +3 text varchar blob1 blob2 +REPAIR TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair status OK +SELECT * FROM t1; +a b c d e +1 text varchar blob1 blob2 +2 text varchar blob1 blob2 +3 text varchar blob1 blob2 +CHECK TABLE t1; +Table Op Msg_type Msg_text +test.t1 check status OK +DROP TABLE t1; +# # BUG#11758979 - 51252: ARCHIVE TABLES STILL FAIL UNDER STRESS # TESTS: CRASH, CORRUPTION, 4G MEMOR # (to be executed with valgrind) @@ -12811,6 +12861,14 @@ Table Op Msg_type Msg_text test.t1 optimize status OK DROP TABLE t1; # +# Bug#13907676: HA_ARCHIVE::INFO +# +CREATE TABLE t1 (a INT) ENGINE=ARCHIVE; +CREATE TABLE t2 SELECT * FROM t1; +SELECT * FROM t2; +a +DROP TABLE t1, t2; +# # BUG#917689 Using wrong archive table causes crash # create table t1 (a int, b char(50)) engine=archive; @@ -12821,7 +12879,6 @@ select * from t1; ERROR HY000: Table 't1' is marked as crashed and should be repaired show warnings; Level Code Message -Warning 127 Got error 127 when reading table `test`.`t1` Error 1194 Table 't1' is marked as crashed and should be repaired drop table t1; create temporary table t1 (a int) engine=archive; @@ -12847,5 +12904,5 @@ Table Create Table t1 CREATE TEMPORARY TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` varchar(10) DEFAULT NULL -) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 DELAY_KEY_WRITE=1 +) ENGINE=ARCHIVE DEFAULT CHARSET=latin1 drop table t1; diff --git a/mysql-test/suite/archive/archive.test b/mysql-test/suite/archive/archive.test index 1114eb4e89a..81a73683541 100644 --- a/mysql-test/suite/archive/archive.test +++ b/mysql-test/suite/archive/archive.test @@ -12,6 +12,7 @@ DROP TABLE if exists t1,t2,t3,t4,t5,t6; --enable_warnings SET storage_engine=ARCHIVE; +let $MYSQLD_DATADIR= `SELECT @@datadir`; CREATE TABLE t1 ( Period smallint(4) unsigned zerofill DEFAULT '0000' NOT NULL, @@ -1643,30 +1644,18 @@ DROP TABLE t1,t2; # # BUG#47012 archive tables are not upgradeable, and server crashes on any access # -let $MYSQLD_DATADIR= `SELECT @@datadir`; - -# Remove files to handle possible restart of test -flush tables; -remove_files_wildcard $MYSQLD_DATADIR/test t1.*; - copy_file std_data/bug47012.frm $MYSQLD_DATADIR/test/t1.frm; copy_file std_data/bug47012.ARZ $MYSQLD_DATADIR/test/t1.ARZ; copy_file std_data/bug47012.ARM $MYSQLD_DATADIR/test/t1.ARM; ---error ER_TABLE_NEEDS_UPGRADE SHOW CREATE TABLE t1; ---error ER_TABLE_NEEDS_UPGRADE SELECT * FROM t1; ---error ER_TABLE_NEEDS_UPGRADE INSERT INTO t1 (col1, col2) VALUES (1, "value"); REPAIR TABLE t1; -SHOW CREATE TABLE t1; -SELECT * FROM t1; DROP TABLE t1; -remove_file $MYSQLD_DATADIR/test/t1.ARM; --echo # --echo # Ensure that TRUNCATE fails for non-empty archive tables. @@ -1700,6 +1689,7 @@ REPAIR TABLE t1 EXTENDED; SELECT * FROM t1; DROP TABLE t1; + --echo # --echo # BUG#57162 - valgrind errors, random data when returning --echo # ordered data from archive tables @@ -1715,6 +1705,35 @@ SELECT * FROM t1 ORDER BY f LIMIT 1; DROP TABLE t1; SET sort_buffer_size=DEFAULT; +--echo # +--echo # BUG#11756687 - 48633: ARCHIVE TABLES ARE NOT UPGRADEABLE +--echo # +copy_file std_data/bug48633.frm $MYSQLD_DATADIR/test/t1.frm; +copy_file std_data/bug48633.ARZ $MYSQLD_DATADIR/test/t1.ARZ; +copy_file std_data/bug48633.ARM $MYSQLD_DATADIR/test/t1.ARM; +SHOW CREATE TABLE t1; +# Test first table scan +SELECT * FROM t1; +# Test second table scan +SELECT * FROM t1; +# Test table close +FLUSH TABLE t1; +SELECT * FROM t1; +# Test check +CHECK TABLE t1; +SELECT * FROM t1; +# Test insert +INSERT INTO t1 VALUES(3, 'text', 'varchar', 'blob1', 'blob2'); +SELECT * FROM t1; +# Test table close after insert +FLUSH TABLE t1; +SELECT * FROM t1; +# Test repair +REPAIR TABLE t1; +SELECT * FROM t1; +# Test check table after upgrade +CHECK TABLE t1; +DROP TABLE t1; --echo # --echo # BUG#11758979 - 51252: ARCHIVE TABLES STILL FAIL UNDER STRESS @@ -1730,6 +1749,14 @@ FLUSH TABLE t1; OPTIMIZE TABLE t1; DROP TABLE t1; +--echo # +--echo # Bug#13907676: HA_ARCHIVE::INFO +--echo # +CREATE TABLE t1 (a INT) ENGINE=ARCHIVE; +CREATE TABLE t2 SELECT * FROM t1; +SELECT * FROM t2; +DROP TABLE t1, t2; + --echo # --echo # BUG#917689 Using wrong archive table causes crash --echo # diff --git a/mysql-test/suite/archive/discover.result b/mysql-test/suite/archive/discover.result index 4701d70273b..a2a0bfcd8d5 100644 --- a/mysql-test/suite/archive/discover.result +++ b/mysql-test/suite/archive/discover.result @@ -86,7 +86,7 @@ flush tables; drop table t0; show status like 'Handler_discover'; Variable_name Value -Handler_discover 7 +Handler_discover 6 # # Bug#45377: ARCHIVE tables aren't discoverable after OPTIMIZE # diff --git a/mysql-test/suite/archive/partition_archive.result b/mysql-test/suite/archive/partition_archive.result index 7b3c01694da..bb3e531a2ed 100644 --- a/mysql-test/suite/archive/partition_archive.result +++ b/mysql-test/suite/archive/partition_archive.result @@ -30,7 +30,7 @@ partition by list (a) (partition p0 values in (1), partition p1 values in (2)); insert into t1 values (1), (2); create index inx on t1 (a); -ERROR HY000: Can't create table `db99`.`#sql-temporary` (errno: 1 "Operation not permitted") +ERROR HY000: Can't create table `db99`.`#sql-temporary` (errno: 140 "Wrong create options") alter table t1 add partition (partition p2 values in (3)); alter table t1 drop partition p2; use test; diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index c0062ddbaaa..8c008ae0bce 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -315,7 +315,7 @@ CREATE TABLE t2 (primary key (a)) engine=innodb select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' DROP TABLE if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' INSERT INTO t1 values (3,3); CREATE TEMPORARY TABLE t2 (primary key (a)) engine=innodb select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -324,7 +324,7 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back DROP TABLE IF EXISTS t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb; INSERT INTO t1 VALUES (4,4); CREATE TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; diff --git a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result index 03fcb481245..10f3f5ddf2a 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result @@ -309,7 +309,7 @@ CREATE TABLE t2 (primary key (a)) engine=innodb select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' DROP TABLE if exists t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' INSERT INTO t1 values (3,3); CREATE TEMPORARY TABLE t2 (primary key (a)) engine=innodb select * from t1; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -318,7 +318,7 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back DROP TABLE IF EXISTS t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'test.t2' CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb; INSERT INTO t1 VALUES (4,4); CREATE TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; diff --git a/mysql-test/suite/csv/csv.result b/mysql-test/suite/csv/csv.result index be8ffa8f9e5..5ac79a23c47 100644 --- a/mysql-test/suite/csv/csv.result +++ b/mysql-test/suite/csv/csv.result @@ -4927,9 +4927,9 @@ period 9410 drop table if exists t1,t2,t3,t4; Warnings: -Note 1051 Unknown table 't2' -Note 1051 Unknown table 't3' -Note 1051 Unknown table 't4' +Note 1051 Unknown table 'test.t2' +Note 1051 Unknown table 'test.t3' +Note 1051 Unknown table 'test.t4' DROP TABLE IF EXISTS bug13894; CREATE TABLE bug13894 ( val integer not null ) ENGINE = CSV; INSERT INTO bug13894 VALUES (5); diff --git a/mysql-test/suite/federated/federated_server.result b/mysql-test/suite/federated/federated_server.result index e2a5d60302b..3855964e2ba 100644 --- a/mysql-test/suite/federated/federated_server.result +++ b/mysql-test/suite/federated/federated_server.result @@ -5,7 +5,7 @@ create database second_db; use first_db; DROP TABLE IF EXISTS first_db.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'first_db.t1' CREATE TABLE first_db.t1 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -13,7 +13,7 @@ CREATE TABLE first_db.t1 ( DEFAULT CHARSET=latin1; DROP TABLE IF EXISTS first_db.t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'first_db.t2' CREATE TABLE first_db.t2 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -22,7 +22,7 @@ DEFAULT CHARSET=latin1; use second_db; DROP TABLE IF EXISTS second_db.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'second_db.t1' CREATE TABLE second_db.t1 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -30,7 +30,7 @@ CREATE TABLE second_db.t1 ( DEFAULT CHARSET=latin1; DROP TABLE IF EXISTS second_db.t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'second_db.t2' CREATE TABLE second_db.t2 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -60,7 +60,7 @@ server_one 127.0.0.1 first_db root SLAVE_PORT mysql root server_two 127.0.0.1 second_db root SLAVE_PORT mysql root DROP TABLE IF EXISTS federated.old; Warnings: -Note 1051 Unknown table 'old' +Note 1051 Unknown table 'federated.old' CREATE TABLE federated.old ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -73,7 +73,7 @@ id name 1 federated.old-> first_db.t1, url format DROP TABLE IF EXISTS federated.old2; Warnings: -Note 1051 Unknown table 'old2' +Note 1051 Unknown table 'federated.old2' CREATE TABLE federated.old2 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -86,7 +86,7 @@ id name 1 federated.old2-> first_db.t2, url format DROP TABLE IF EXISTS federated.urldb2t1; Warnings: -Note 1051 Unknown table 'urldb2t1' +Note 1051 Unknown table 'federated.urldb2t1' CREATE TABLE federated.urldb2t1 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -99,7 +99,7 @@ id name 1 federated.urldb2t1 -> second_db.t1, url format DROP TABLE IF EXISTS federated.urldb2t2; Warnings: -Note 1051 Unknown table 'urldb2t2' +Note 1051 Unknown table 'federated.urldb2t2' CREATE TABLE federated.urldb2t2 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -112,7 +112,7 @@ id name 1 federated.urldb2t2 -> second_db.t2, url format DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' @@ -126,7 +126,7 @@ id name 1 server_one, new scheme, first_db.t1 DROP TABLE IF EXISTS federated.whatever; Warnings: -Note 1051 Unknown table 'whatever' +Note 1051 Unknown table 'federated.whatever' CREATE TABLE federated.whatever ( `id` int(20) NOT NULL, `name` varchar(64) NOT NULL default '' diff --git a/mysql-test/suite/federated/federated_transactions.result b/mysql-test/suite/federated/federated_transactions.result index 2b88f4d0f36..52a0686741e 100644 --- a/mysql-test/suite/federated/federated_transactions.result +++ b/mysql-test/suite/federated/federated_transactions.result @@ -2,7 +2,7 @@ CREATE DATABASE federated; CREATE DATABASE federated; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -10,7 +10,7 @@ CREATE TABLE federated.t1 ( DEFAULT CHARSET=latin1 ENGINE=innodb; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' diff --git a/mysql-test/suite/federated/federatedx.result b/mysql-test/suite/federated/federatedx.result index 5ae2abb46e5..c93ff1493d9 100644 --- a/mysql-test/suite/federated/federatedx.result +++ b/mysql-test/suite/federated/federatedx.result @@ -6,7 +6,7 @@ SET @OLD_SLAVE_CONCURRENT_INSERT= @@GLOBAL.CONCURRENT_INSERT; SET @@GLOBAL.CONCURRENT_INSERT= 0; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `group` int NOT NULL default 0, @@ -17,7 +17,7 @@ CREATE TABLE federated.t1 ( DEFAULT CHARSET=latin1; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `group` int NOT NULL default 0, @@ -50,7 +50,7 @@ CONNECTION='mysql://root@127.0.0.1:SLAVE_PORT/federated/t3'; ERROR HY000: Can't create federated table. Foreign data src error: database: 'federated' username: 'root' hostname: '127.0.0.1' DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `group` int NOT NULL default 0, @@ -63,7 +63,7 @@ CONNECTION='mysql://user:pass@127.0.0.1:SLAVE_PORT/federated/t1'; ERROR HY000: Can't create federated table. Foreign data src error: database: 'federated' username: 'user' hostname: '127.0.0.1' DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `group` int NOT NULL default 0, @@ -89,7 +89,7 @@ DELETE FROM federated.t1; DROP TABLE federated.t1; DROP TABLE IF EXISTS federated.t2; Warnings: -Note 1051 Unknown table 't2' +Note 1051 Unknown table 'federated.t2' CREATE TABLE federated.t2 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -112,7 +112,7 @@ DROP TABLE federated.t2; DROP TABLE IF EXISTS federated.t1; DROP TABLE IF EXISTS federated.`t1%`; Warnings: -Note 1051 Unknown table 't1%' +Note 1051 Unknown table 'federated.t1%' CREATE TABLE federated.`t1%` ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -120,7 +120,7 @@ CREATE TABLE federated.`t1%` ( DEFAULT CHARSET=latin1; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, `name` varchar(32) NOT NULL default '' @@ -152,7 +152,7 @@ DROP TABLE IF EXISTS federated.`t1%`; DROP TABLE IF EXISTS federated.`t1%`; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL auto_increment, `name` varchar(32) NOT NULL default '', @@ -1391,7 +1391,7 @@ PRIMARY KEY (`id`), key (country_id)); DROP TABLE IF EXISTS federated.countries; Warnings: -Note 1051 Unknown table 'countries' +Note 1051 Unknown table 'federated.countries' CREATE TABLE federated.countries ( `id` int(20) NOT NULL auto_increment, `country` varchar(32), @@ -1544,13 +1544,13 @@ drop table federated.t1; drop table federated.t1; DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL auto_increment, PRIMARY KEY (`id`)); DROP TABLE IF EXISTS federated.t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'federated.t1' CREATE TABLE federated.t1 ( `id` int(20) NOT NULL auto_increment, PRIMARY KEY (`id`) diff --git a/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result b/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result index 126d6c2080a..8b96e54eefd 100644 --- a/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result +++ b/mysql-test/suite/funcs_1/r/innodb_storedproc_02.result @@ -347,8 +347,6 @@ CALL h1(); -7- 1 1 1 1 1 1 END x1 x2 x3 x4 x5 x6 END 1 1 1 1 1 1 -Warnings: -Error 1062 Duplicate entry 'a' for key 'w' DROP TABLE IF EXISTS tnull; DROP PROCEDURE IF EXISTS sp1; CREATE TABLE tnull(f1 int); @@ -447,8 +445,6 @@ END// CALL h2(); x1 x2 x3 x4 x5 x6 1 1 1 1 1 1 -Warnings: -Error 1062 Duplicate entry 'a' for key 'w' SELECT * FROM res_t1; w x a b @@ -554,8 +550,6 @@ exit handler 2 exit handler 2 exit handler 1 exit handler 1 -Warnings: -Error 1146 Table 'db_storedproc.tqq' doesn't exist create table res_t1(w char unique, x char); insert into res_t1 values ('a', 'b'); CREATE PROCEDURE h1 () @@ -586,8 +580,6 @@ END// CALL h1(); x1 x2 x3 x4 x5 x6 1 1 1 1 1 1 -Warnings: -Error 1062 Duplicate entry 'a' for key 'w' This will fail, SQLSTATE 00000 is not allowed CREATE PROCEDURE sp1() begin1_label:BEGIN @@ -631,8 +623,6 @@ CALL sp2(); NULL @x2 @x 1 2 -Warnings: -Error 1318 Incorrect number of arguments for PROCEDURE db_storedproc.sp1; expected 2, got 1 DROP PROCEDURE sp1; DROP PROCEDURE sp2; @@ -664,8 +654,6 @@ ERROR 42000: Incorrect number of arguments for PROCEDURE db_storedproc.sp1; expe CALL sp2(); -1- @x2 @x -1- 0 1 -Warnings: -Error 1318 Incorrect number of arguments for PROCEDURE db_storedproc.sp1; expected 2, got 1 SELECT '-3-', @x2, @x; -3- @x2 @x -3- 1 1 @@ -708,8 +696,6 @@ CALL sp2(); -2- 1 20 -4- @x2 @x -4- 11 22 -Warnings: -Error 1318 Incorrect number of arguments for PROCEDURE db_storedproc.sp1; expected 2, got 1 DROP PROCEDURE sp1; DROP PROCEDURE sp2; @@ -776,33 +762,21 @@ SELECT @done, @x; 0 1 INSERT INTO temp VALUES('1', NULL); CALL sp1(); -Warnings: -Warning 1265 Data truncated for column 'f1' at row 1 -Warning 1265 Data truncated for column 'f2' at row 1 SELECT @done, @x; @done @x 1 1 INSERT INTO temp VALUES('2', NULL); CALL sp2(); -Warnings: -Warning 1265 Data truncated for column 'f1' at row 1 -Warning 1265 Data truncated for column 'f2' at row 1 SELECT @done, @x; @done @x 1 1 INSERT INTO temp VALUES('3', NULL); CALL sp3(); -Warnings: -Warning 1265 Data truncated for column 'f1' at row 1 -Warning 1265 Data truncated for column 'f2' at row 1 SELECT @done, @x; @done @x 1 0 INSERT INTO temp VALUES('4', NULL); CALL sp4(); -Warnings: -Warning 1265 Data truncated for column 'f1' at row 1 -Warning 1265 Data truncated for column 'f2' at row 1 SELECT @done, @x; @done @x 1 0 @@ -911,26 +885,18 @@ SELECT @done, @x; @done @x 0 1 CALL sp1(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed SELECT @done, @x; @done @x 1 2 CALL sp2(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed SELECT @done, @x; @done @x 1 2 CALL sp3(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed SELECT @done, @x; @done @x 1 1 CALL sp4(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed SELECT @done, @x; @done @x 1 1 @@ -1065,8 +1031,6 @@ SQLSTATE 21000 SQLSTATE 24000 -Warnings: -Error 1326 Cursor is not open SELECT '-1-', @x; -1- @x -1- 6 @@ -1077,24 +1041,18 @@ SQLSTATE SQLEXCEPTION SQLSTATE 24000 -Warnings: -Error 1326 Cursor is not open SELECT '-2-', @x; -2- @x -2- 6 CALL sp3(); SQLSTATE 20000 -Warnings: -Error 1339 Case not found for CASE statement SELECT '-3-', @x; -3- @x -3- 1 CALL sp4(); SQLSTATE SQLEXCEPTION -Warnings: -Error 1339 Case not found for CASE statement SELECT '-4-', @x; -4- @x -4- 1 @@ -1377,8 +1335,6 @@ CLOSE cur1; CLOSE cur2; END// CALL sp_outer(); -Warnings: -Error 1329 No data - zero rows fetched, selected, or processed SELECT * FROM temp1; f0 cnt f1 f2 f3 f4 _sp_out_ 1 a` a` 1000-01-01 -5000 diff --git a/mysql-test/suite/funcs_1/r/innodb_trig_0102.result b/mysql-test/suite/funcs_1/r/innodb_trig_0102.result index 330a9a315ee..77fabeaef77 100644 --- a/mysql-test/suite/funcs_1/r/innodb_trig_0102.result +++ b/mysql-test/suite/funcs_1/r/innodb_trig_0102.result @@ -185,7 +185,7 @@ Testcase 3.5.1.7: - need to fix ------------------------------- drop table if exists t1; Warnings: -Note 1051 Unknown table 't1' +Note 1051 Unknown table 'test.t1' create table t1 (f1 int, f2 char(25),f3 int) engine = ; CREATE TRIGGER trg5_1 BEFORE INSERT on test.t1 for each row set new.f3 = '14'; diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result index 7b7b13c6a8c..e2fba35c7f8 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result @@ -72,7 +72,7 @@ def mysql general_log argument 6 NULL NO mediumtext 16777215 16777215 NULL NULL def mysql general_log command_type 5 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select,insert,update,references def mysql general_log event_time 1 CURRENT_TIMESTAMP(6) NO timestamp NULL NULL NULL NULL 6 NULL NULL timestamp(6) on update CURRENT_TIMESTAMP select,insert,update,references def mysql general_log server_id 4 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references -def mysql general_log thread_id 3 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) select,insert,update,references +def mysql general_log thread_id 3 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(21) unsigned select,insert,update,references def mysql general_log user_host 2 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext select,insert,update,references def mysql gtid_slave_pos domain_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI select,insert,update,references def mysql gtid_slave_pos seq_no 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references @@ -117,13 +117,6 @@ def mysql index_stats db_name 1 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_ def mysql index_stats index_name 3 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_bin varchar(64) PRI select,insert,update,references def mysql index_stats prefix_arity 4 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned PRI select,insert,update,references def mysql index_stats table_name 2 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_bin varchar(64) PRI select,insert,update,references -def mysql ndb_binlog_index deletes 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql ndb_binlog_index epoch 3 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned PRI select,insert,update,references -def mysql ndb_binlog_index File 2 NULL NO varchar 255 255 NULL NULL NULL latin1 latin1_swedish_ci varchar(255) select,insert,update,references -def mysql ndb_binlog_index inserts 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql ndb_binlog_index Position 1 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql ndb_binlog_index schemaops 7 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql ndb_binlog_index updates 5 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references def mysql plugin dl 2 NO varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128) select,insert,update,references def mysql plugin name 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) PRI select,insert,update,references def mysql proc body 11 NULL NO longblob 4294967295 4294967295 NULL NULL NULL NULL NULL longblob select,insert,update,references @@ -170,50 +163,6 @@ def mysql servers Server_name 1 NO char 64 192 NULL NULL NULL utf8 utf8_general def mysql servers Socket 7 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Username 4 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Wrapper 8 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references -def mysql slave_master_info Bind 18 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references Displays which interface is employed when connecting to the MySQL server -def mysql slave_master_info Connect_retry 9 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references The period (in seconds) that the slave will wait before trying to reconnect to the master. -def mysql slave_master_info Enabled_ssl 10 NULL NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) select,insert,update,references Indicates whether the server supports SSL connections. -def mysql slave_master_info Heartbeat 17 NULL NO float NULL NULL 12 NULL NULL NULL NULL float select,insert,update,references -def mysql slave_master_info Host 5 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The host name of the master. -def mysql slave_master_info Ignored_server_ids 19 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The number of server IDs to be ignored, followed by the actual server IDs -def mysql slave_master_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI select,insert,update,references -def mysql slave_master_info Master_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the master binary log currently being read from the master. -def mysql slave_master_info Master_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references The master log position of the last read event. -def mysql slave_master_info Number_of_lines 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references Number of lines in the file. -def mysql slave_master_info Port 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references The network port used to connect to the master. -def mysql slave_master_info Retry_count 21 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references Number of reconnect attempts, to the master, before giving up. -def mysql slave_master_info Ssl_ca 11 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The file used for the Certificate Authority (CA) certificate. -def mysql slave_master_info Ssl_capath 12 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The path to the Certificate Authority (CA) certificates. -def mysql slave_master_info Ssl_cert 13 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the SSL certificate file. -def mysql slave_master_info Ssl_cipher 14 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the cipher in use for the SSL connection. -def mysql slave_master_info Ssl_crl 22 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The file used for the Certificate Revocation List (CRL) -def mysql slave_master_info Ssl_crlpath 23 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The path used for Certificate Revocation List (CRL) files -def mysql slave_master_info Ssl_key 15 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the SSL key file. -def mysql slave_master_info Ssl_verify_server_cert 16 NULL NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) select,insert,update,references Whether to verify the server certificate. -def mysql slave_master_info User_name 6 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The user name used to connect to the master. -def mysql slave_master_info User_password 7 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The password used to connect to the master. -def mysql slave_master_info Uuid 20 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The master server uuid. -def mysql slave_relay_log_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI select,insert,update,references -def mysql slave_relay_log_info Master_log_name 5 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the master binary log file from which the events in the relay log file were read. -def mysql slave_relay_log_info Master_log_pos 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references The master log position of the last executed event. -def mysql slave_relay_log_info Number_of_lines 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references Number of lines in the file or rows in the table. Used to version table definitions. -def mysql slave_relay_log_info Number_of_workers 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references -def mysql slave_relay_log_info Relay_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references The name of the current relay log file. -def mysql slave_relay_log_info Relay_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references The relay log position of the last executed event. -def mysql slave_relay_log_info Sql_delay 7 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) select,insert,update,references The number of seconds that the slave must lag behind the master. -def mysql slave_worker_info Checkpoint_group_bitmap 13 NULL NO blob 65535 65535 NULL NULL NULL NULL NULL blob select,insert,update,references -def mysql slave_worker_info Checkpoint_group_size 12 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references -def mysql slave_worker_info Checkpoint_master_log_name 9 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references -def mysql slave_worker_info Checkpoint_master_log_pos 10 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql slave_worker_info Checkpoint_relay_log_name 7 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references -def mysql slave_worker_info Checkpoint_relay_log_pos 8 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql slave_worker_info Checkpoint_seqno 11 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references -def mysql slave_worker_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI select,insert,update,references -def mysql slave_worker_info Master_log_name 5 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references -def mysql slave_worker_info Master_log_pos 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql slave_worker_info Relay_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text select,insert,update,references -def mysql slave_worker_info Relay_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned select,insert,update,references -def mysql slave_worker_info Worker_id 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI select,insert,update,references def mysql slow_log db 7 NULL NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select,insert,update,references def mysql slow_log insert_id 9 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) select,insert,update,references def mysql slow_log last_insert_id 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) select,insert,update,references @@ -224,6 +173,7 @@ def mysql slow_log rows_sent 5 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) def mysql slow_log server_id 10 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned select,insert,update,references def mysql slow_log sql_text 11 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext select,insert,update,references def mysql slow_log start_time 1 CURRENT_TIMESTAMP(6) NO timestamp NULL NULL NULL NULL 6 NULL NULL timestamp(6) on update CURRENT_TIMESTAMP select,insert,update,references +def mysql slow_log thread_id 12 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(21) unsigned select,insert,update,references def mysql slow_log user_host 2 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext select,insert,update,references def mysql tables_priv Column_priv 8 NO set 31 93 NULL NULL NULL utf8 utf8_general_ci set('Select','Insert','Update','References') select,insert,update,references def mysql tables_priv Db 2 NO char 64 192 NULL NULL NULL utf8 utf8_bin char(64) PRI select,insert,update,references @@ -274,6 +224,7 @@ def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) u def mysql user max_updates 38 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned select,insert,update,references def mysql user max_user_connections 40 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) select,insert,update,references def mysql user Password 3 NO char 41 41 NULL NULL NULL latin1 latin1_bin char(41) select,insert,update,references +def mysql user password_expired 43 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user plugin 41 NO char 64 64 NULL NULL NULL latin1 latin1_swedish_ci char(64) select,insert,update,references def mysql user Process_priv 12 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user References_priv 15 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references @@ -310,7 +261,6 @@ COL_CML DATA_TYPE CHARACTER_SET_NAME COLLATION_NAME 1.0000 varbinary NULL NULL 1.0000 char latin1 latin1_bin 1.0000 char latin1 latin1_swedish_ci -1.0000 varchar latin1 latin1_swedish_ci 1.0000 text utf8 utf8_bin 1.0000 mediumtext utf8 utf8_general_ci 1.0000 text utf8 utf8_general_ci @@ -344,7 +294,6 @@ COL_CML DATA_TYPE CHARACTER_SET_NAME COLLATION_NAME NULL bigint NULL NULL NULL datetime NULL NULL NULL decimal NULL NULL -NULL float NULL NULL NULL int NULL NULL NULL smallint NULL NULL NULL time NULL NULL @@ -434,7 +383,7 @@ NULL mysql func ret tinyint NULL NULL NULL NULL tinyint(1) 3.0000 mysql func type enum 9 27 utf8 utf8_general_ci enum('function','aggregate') NULL mysql general_log event_time timestamp NULL NULL NULL NULL timestamp(6) 1.0000 mysql general_log user_host mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext -NULL mysql general_log thread_id int NULL NULL NULL NULL int(11) +NULL mysql general_log thread_id bigint NULL NULL NULL NULL bigint(21) unsigned NULL mysql general_log server_id int NULL NULL NULL NULL int(10) unsigned 3.0000 mysql general_log command_type varchar 64 192 utf8 utf8_general_ci varchar(64) 1.0000 mysql general_log argument mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext @@ -481,13 +430,6 @@ NULL mysql help_topic help_category_id smallint NULL NULL NULL NULL smallint(5) 3.0000 mysql index_stats index_name varchar 64 192 utf8 utf8_bin varchar(64) NULL mysql index_stats prefix_arity int NULL NULL NULL NULL int(11) unsigned NULL mysql index_stats avg_frequency decimal NULL NULL NULL NULL decimal(12,4) -NULL mysql ndb_binlog_index Position bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql ndb_binlog_index File varchar 255 255 latin1 latin1_swedish_ci varchar(255) -NULL mysql ndb_binlog_index epoch bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index inserts bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index updates bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index deletes bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index schemaops bigint NULL NULL NULL NULL bigint(20) unsigned 3.0000 mysql plugin name varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 mysql plugin dl varchar 128 384 utf8 utf8_general_ci varchar(128) 3.0000 mysql proc db char 64 192 utf8 utf8_bin char(64) @@ -534,50 +476,6 @@ NULL mysql servers Port int NULL NULL NULL NULL int(4) 3.0000 mysql servers Socket char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Wrapper char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Owner char 64 192 utf8 utf8_general_ci char(64) -NULL mysql slave_master_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Number_of_lines int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_master_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_master_info Host text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info User_name text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info User_password text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Port int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Connect_retry int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Enabled_ssl tinyint NULL NULL NULL NULL tinyint(1) -1.0000 mysql slave_master_info Ssl_ca text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_capath text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_cert text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_cipher text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_key text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Ssl_verify_server_cert tinyint NULL NULL NULL NULL tinyint(1) -NULL mysql slave_master_info Heartbeat float NULL NULL NULL NULL float -1.0000 mysql slave_master_info Bind text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ignored_server_ids text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Uuid text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Retry_count bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_master_info Ssl_crl text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_crlpath text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_relay_log_info Number_of_lines int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_relay_log_info Relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_relay_log_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql slave_relay_log_info Sql_delay int NULL NULL NULL NULL int(11) -NULL mysql slave_relay_log_info Number_of_workers int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Worker_id int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_worker_info Relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Checkpoint_relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Checkpoint_relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Checkpoint_master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Checkpoint_master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql slave_worker_info Checkpoint_seqno int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Checkpoint_group_size int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_worker_info Checkpoint_group_bitmap blob 65535 65535 NULL NULL blob NULL mysql slow_log start_time timestamp NULL NULL NULL NULL timestamp(6) 1.0000 mysql slow_log user_host mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext NULL mysql slow_log query_time time NULL NULL NULL NULL time(6) @@ -589,6 +487,7 @@ NULL mysql slow_log last_insert_id int NULL NULL NULL NULL int(11) NULL mysql slow_log insert_id int NULL NULL NULL NULL int(11) NULL mysql slow_log server_id int NULL NULL NULL NULL int(10) unsigned 1.0000 mysql slow_log sql_text mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext +NULL mysql slow_log thread_id bigint NULL NULL NULL NULL bigint(21) unsigned 3.0000 mysql tables_priv Host char 60 180 utf8 utf8_bin char(60) 3.0000 mysql tables_priv Db char 64 192 utf8 utf8_bin char(64) 3.0000 mysql tables_priv User char 16 48 utf8 utf8_bin char(16) @@ -656,3 +555,4 @@ NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) 1.0000 mysql user plugin char 64 64 latin1 latin1_swedish_ci char(64) 1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text +3.0000 mysql user password_expired enum 1 3 utf8 utf8_general_ci enum('N','Y') diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result index 4cb5ee917a4..94e1ed45152 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result +++ b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result @@ -72,7 +72,7 @@ def mysql general_log argument 6 NULL NO mediumtext 16777215 16777215 NULL NULL def mysql general_log command_type 5 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) def mysql general_log event_time 1 CURRENT_TIMESTAMP(6) NO timestamp NULL NULL NULL NULL 6 NULL NULL timestamp(6) on update CURRENT_TIMESTAMP def mysql general_log server_id 4 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned -def mysql general_log thread_id 3 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) +def mysql general_log thread_id 3 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(21) unsigned def mysql general_log user_host 2 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext def mysql gtid_slave_pos domain_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI def mysql gtid_slave_pos seq_no 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned @@ -117,13 +117,6 @@ def mysql index_stats db_name 1 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_ def mysql index_stats index_name 3 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_bin varchar(64) PRI def mysql index_stats prefix_arity 4 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned PRI def mysql index_stats table_name 2 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_bin varchar(64) PRI -def mysql ndb_binlog_index deletes 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql ndb_binlog_index epoch 3 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned PRI -def mysql ndb_binlog_index File 2 NULL NO varchar 255 255 NULL NULL NULL latin1 latin1_swedish_ci varchar(255) -def mysql ndb_binlog_index inserts 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql ndb_binlog_index Position 1 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql ndb_binlog_index schemaops 7 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql ndb_binlog_index updates 5 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned def mysql plugin dl 2 NO varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128) def mysql plugin name 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) PRI def mysql proc body 11 NULL NO longblob 4294967295 4294967295 NULL NULL NULL NULL NULL longblob @@ -170,50 +163,6 @@ def mysql servers Server_name 1 NO char 64 192 NULL NULL NULL utf8 utf8_general def mysql servers Socket 7 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) def mysql servers Username 4 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) def mysql servers Wrapper 8 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) -def mysql slave_master_info Bind 18 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text Displays which interface is employed when connecting to the MySQL server -def mysql slave_master_info Connect_retry 9 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned The period (in seconds) that the slave will wait before trying to reconnect to the master. -def mysql slave_master_info Enabled_ssl 10 NULL NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) Indicates whether the server supports SSL connections. -def mysql slave_master_info Heartbeat 17 NULL NO float NULL NULL 12 NULL NULL NULL NULL float -def mysql slave_master_info Host 5 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The host name of the master. -def mysql slave_master_info Ignored_server_ids 19 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The number of server IDs to be ignored, followed by the actual server IDs -def mysql slave_master_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI -def mysql slave_master_info Master_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the master binary log currently being read from the master. -def mysql slave_master_info Master_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned The master log position of the last read event. -def mysql slave_master_info Number_of_lines 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned Number of lines in the file. -def mysql slave_master_info Port 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned The network port used to connect to the master. -def mysql slave_master_info Retry_count 21 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned Number of reconnect attempts, to the master, before giving up. -def mysql slave_master_info Ssl_ca 11 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The file used for the Certificate Authority (CA) certificate. -def mysql slave_master_info Ssl_capath 12 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The path to the Certificate Authority (CA) certificates. -def mysql slave_master_info Ssl_cert 13 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the SSL certificate file. -def mysql slave_master_info Ssl_cipher 14 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the cipher in use for the SSL connection. -def mysql slave_master_info Ssl_crl 22 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The file used for the Certificate Revocation List (CRL) -def mysql slave_master_info Ssl_crlpath 23 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The path used for Certificate Revocation List (CRL) files -def mysql slave_master_info Ssl_key 15 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the SSL key file. -def mysql slave_master_info Ssl_verify_server_cert 16 NULL NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) Whether to verify the server certificate. -def mysql slave_master_info User_name 6 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The user name used to connect to the master. -def mysql slave_master_info User_password 7 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The password used to connect to the master. -def mysql slave_master_info Uuid 20 NULL YES text 65535 65535 NULL NULL NULL utf8 utf8_bin text The master server uuid. -def mysql slave_relay_log_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI -def mysql slave_relay_log_info Master_log_name 5 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the master binary log file from which the events in the relay log file were read. -def mysql slave_relay_log_info Master_log_pos 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned The master log position of the last executed event. -def mysql slave_relay_log_info Number_of_lines 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned Number of lines in the file or rows in the table. Used to version table definitions. -def mysql slave_relay_log_info Number_of_workers 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned -def mysql slave_relay_log_info Relay_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text The name of the current relay log file. -def mysql slave_relay_log_info Relay_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned The relay log position of the last executed event. -def mysql slave_relay_log_info Sql_delay 7 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) The number of seconds that the slave must lag behind the master. -def mysql slave_worker_info Checkpoint_group_bitmap 13 NULL NO blob 65535 65535 NULL NULL NULL NULL NULL blob -def mysql slave_worker_info Checkpoint_group_size 12 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned -def mysql slave_worker_info Checkpoint_master_log_name 9 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text -def mysql slave_worker_info Checkpoint_master_log_pos 10 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql slave_worker_info Checkpoint_relay_log_name 7 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text -def mysql slave_worker_info Checkpoint_relay_log_pos 8 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql slave_worker_info Checkpoint_seqno 11 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned -def mysql slave_worker_info Master_id 1 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI -def mysql slave_worker_info Master_log_name 5 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text -def mysql slave_worker_info Master_log_pos 6 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql slave_worker_info Relay_log_name 3 NULL NO text 65535 65535 NULL NULL NULL utf8 utf8_bin text -def mysql slave_worker_info Relay_log_pos 4 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(20) unsigned -def mysql slave_worker_info Worker_id 2 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned PRI def mysql slow_log db 7 NULL NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) def mysql slow_log insert_id 9 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) def mysql slow_log last_insert_id 8 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) @@ -224,6 +173,7 @@ def mysql slow_log rows_sent 5 NULL NO int NULL NULL 10 0 NULL NULL NULL int(11) def mysql slow_log server_id 10 NULL NO int NULL NULL 10 0 NULL NULL NULL int(10) unsigned def mysql slow_log sql_text 11 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext def mysql slow_log start_time 1 CURRENT_TIMESTAMP(6) NO timestamp NULL NULL NULL NULL 6 NULL NULL timestamp(6) on update CURRENT_TIMESTAMP +def mysql slow_log thread_id 12 NULL NO bigint NULL NULL 20 0 NULL NULL NULL bigint(21) unsigned def mysql slow_log user_host 2 NULL NO mediumtext 16777215 16777215 NULL NULL NULL utf8 utf8_general_ci mediumtext def mysql tables_priv Column_priv 8 NO set 31 93 NULL NULL NULL utf8 utf8_general_ci set('Select','Insert','Update','References') def mysql tables_priv Db 2 NO char 64 192 NULL NULL NULL utf8 utf8_bin char(64) PRI @@ -274,6 +224,7 @@ def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) u def mysql user max_updates 38 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned def mysql user max_user_connections 40 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) def mysql user Password 3 NO char 41 41 NULL NULL NULL latin1 latin1_bin char(41) +def mysql user password_expired 43 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') def mysql user plugin 41 NO char 64 64 NULL NULL NULL latin1 latin1_swedish_ci char(64) def mysql user Process_priv 12 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') def mysql user References_priv 15 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') @@ -310,7 +261,6 @@ COL_CML DATA_TYPE CHARACTER_SET_NAME COLLATION_NAME 1.0000 varbinary NULL NULL 1.0000 char latin1 latin1_bin 1.0000 char latin1 latin1_swedish_ci -1.0000 varchar latin1 latin1_swedish_ci 1.0000 text utf8 utf8_bin 1.0000 mediumtext utf8 utf8_general_ci 1.0000 text utf8 utf8_general_ci @@ -344,7 +294,6 @@ COL_CML DATA_TYPE CHARACTER_SET_NAME COLLATION_NAME NULL bigint NULL NULL NULL datetime NULL NULL NULL decimal NULL NULL -NULL float NULL NULL NULL int NULL NULL NULL smallint NULL NULL NULL time NULL NULL @@ -434,7 +383,7 @@ NULL mysql func ret tinyint NULL NULL NULL NULL tinyint(1) 3.0000 mysql func type enum 9 27 utf8 utf8_general_ci enum('function','aggregate') NULL mysql general_log event_time timestamp NULL NULL NULL NULL timestamp(6) 1.0000 mysql general_log user_host mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext -NULL mysql general_log thread_id int NULL NULL NULL NULL int(11) +NULL mysql general_log thread_id bigint NULL NULL NULL NULL bigint(21) unsigned NULL mysql general_log server_id int NULL NULL NULL NULL int(10) unsigned 3.0000 mysql general_log command_type varchar 64 192 utf8 utf8_general_ci varchar(64) 1.0000 mysql general_log argument mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext @@ -481,13 +430,6 @@ NULL mysql help_topic help_category_id smallint NULL NULL NULL NULL smallint(5) 3.0000 mysql index_stats index_name varchar 64 192 utf8 utf8_bin varchar(64) NULL mysql index_stats prefix_arity int NULL NULL NULL NULL int(11) unsigned NULL mysql index_stats avg_frequency decimal NULL NULL NULL NULL decimal(12,4) -NULL mysql ndb_binlog_index Position bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql ndb_binlog_index File varchar 255 255 latin1 latin1_swedish_ci varchar(255) -NULL mysql ndb_binlog_index epoch bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index inserts bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index updates bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index deletes bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql ndb_binlog_index schemaops bigint NULL NULL NULL NULL bigint(20) unsigned 3.0000 mysql plugin name varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 mysql plugin dl varchar 128 384 utf8 utf8_general_ci varchar(128) 3.0000 mysql proc db char 64 192 utf8 utf8_bin char(64) @@ -534,50 +476,6 @@ NULL mysql servers Port int NULL NULL NULL NULL int(4) 3.0000 mysql servers Socket char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Wrapper char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Owner char 64 192 utf8 utf8_general_ci char(64) -NULL mysql slave_master_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Number_of_lines int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_master_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_master_info Host text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info User_name text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info User_password text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Port int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Connect_retry int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_master_info Enabled_ssl tinyint NULL NULL NULL NULL tinyint(1) -1.0000 mysql slave_master_info Ssl_ca text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_capath text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_cert text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_cipher text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_key text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Ssl_verify_server_cert tinyint NULL NULL NULL NULL tinyint(1) -NULL mysql slave_master_info Heartbeat float NULL NULL NULL NULL float -1.0000 mysql slave_master_info Bind text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ignored_server_ids text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Uuid text 65535 65535 utf8 utf8_bin text -NULL mysql slave_master_info Retry_count bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_master_info Ssl_crl text 65535 65535 utf8 utf8_bin text -1.0000 mysql slave_master_info Ssl_crlpath text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_relay_log_info Number_of_lines int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_relay_log_info Relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_relay_log_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_relay_log_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql slave_relay_log_info Sql_delay int NULL NULL NULL NULL int(11) -NULL mysql slave_relay_log_info Number_of_workers int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Master_id int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Worker_id int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_worker_info Relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Checkpoint_relay_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Checkpoint_relay_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -1.0000 mysql slave_worker_info Checkpoint_master_log_name text 65535 65535 utf8 utf8_bin text -NULL mysql slave_worker_info Checkpoint_master_log_pos bigint NULL NULL NULL NULL bigint(20) unsigned -NULL mysql slave_worker_info Checkpoint_seqno int NULL NULL NULL NULL int(10) unsigned -NULL mysql slave_worker_info Checkpoint_group_size int NULL NULL NULL NULL int(10) unsigned -1.0000 mysql slave_worker_info Checkpoint_group_bitmap blob 65535 65535 NULL NULL blob NULL mysql slow_log start_time timestamp NULL NULL NULL NULL timestamp(6) 1.0000 mysql slow_log user_host mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext NULL mysql slow_log query_time time NULL NULL NULL NULL time(6) @@ -589,6 +487,7 @@ NULL mysql slow_log last_insert_id int NULL NULL NULL NULL int(11) NULL mysql slow_log insert_id int NULL NULL NULL NULL int(11) NULL mysql slow_log server_id int NULL NULL NULL NULL int(10) unsigned 1.0000 mysql slow_log sql_text mediumtext 16777215 16777215 utf8 utf8_general_ci mediumtext +NULL mysql slow_log thread_id bigint NULL NULL NULL NULL bigint(21) unsigned 3.0000 mysql tables_priv Host char 60 180 utf8 utf8_bin char(60) 3.0000 mysql tables_priv Db char 64 192 utf8 utf8_bin char(64) 3.0000 mysql tables_priv User char 16 48 utf8 utf8_bin char(16) @@ -656,3 +555,4 @@ NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) 1.0000 mysql user plugin char 64 64 latin1 latin1_swedish_ci char(64) 1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text +3.0000 mysql user password_expired enum 1 3 utf8 utf8_general_ci enum('N','Y') diff --git a/mysql-test/suite/funcs_1/r/is_key_column_usage.result b/mysql-test/suite/funcs_1/r/is_key_column_usage.result index 66967267202..75cd59604ff 100644 --- a/mysql-test/suite/funcs_1/r/is_key_column_usage.result +++ b/mysql-test/suite/funcs_1/r/is_key_column_usage.result @@ -109,11 +109,8 @@ def mysql PRIMARY def mysql innodb_index_stats database_name def mysql PRIMARY def mysql innodb_index_stats table_name def mysql PRIMARY def mysql innodb_index_stats index_name def mysql PRIMARY def mysql innodb_index_stats stat_name -def mysql innodb_index_stats_ibfk_1 def mysql innodb_index_stats database_name -def mysql innodb_index_stats_ibfk_1 def mysql innodb_index_stats table_name def mysql PRIMARY def mysql innodb_table_stats database_name def mysql PRIMARY def mysql innodb_table_stats table_name -def mysql PRIMARY def mysql ndb_binlog_index epoch def mysql PRIMARY def mysql plugin name def mysql PRIMARY def mysql proc db def mysql PRIMARY def mysql proc name @@ -128,10 +125,6 @@ def mysql PRIMARY def mysql proxies_priv User def mysql PRIMARY def mysql proxies_priv Proxied_host def mysql PRIMARY def mysql proxies_priv Proxied_user def mysql PRIMARY def mysql servers Server_name -def mysql PRIMARY def mysql slave_master_info Master_id -def mysql PRIMARY def mysql slave_relay_log_info Master_id -def mysql PRIMARY def mysql slave_worker_info Master_id -def mysql PRIMARY def mysql slave_worker_info Worker_id def mysql PRIMARY def mysql table_stats db_name def mysql PRIMARY def mysql table_stats table_name def mysql PRIMARY def mysql tables_priv Host diff --git a/mysql-test/suite/funcs_1/r/is_statistics.result b/mysql-test/suite/funcs_1/r/is_statistics.result index 73407fa0a5a..c1afeb8db9b 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics.result +++ b/mysql-test/suite/funcs_1/r/is_statistics.result @@ -116,7 +116,6 @@ def mysql index_stats mysql PRIMARY def mysql index_stats mysql PRIMARY def mysql index_stats mysql PRIMARY def mysql index_stats mysql PRIMARY -def mysql ndb_binlog_index mysql PRIMARY def mysql plugin mysql PRIMARY def mysql proc mysql PRIMARY def mysql proc mysql PRIMARY @@ -133,10 +132,6 @@ def mysql proxies_priv mysql PRIMARY def mysql proxies_priv mysql PRIMARY def mysql proxies_priv mysql Grantor def mysql servers mysql PRIMARY -def mysql slave_master_info mysql PRIMARY -def mysql slave_relay_log_info mysql PRIMARY -def mysql slave_worker_info mysql PRIMARY -def mysql slave_worker_info mysql PRIMARY def mysql table_stats mysql PRIMARY def mysql table_stats mysql PRIMARY def mysql tables_priv mysql PRIMARY diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result index 1bee7c44cb1..5dbf3bc1488 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result @@ -44,7 +44,6 @@ def mysql innodb_index_stats 0 mysql PRIMARY 3 index_name A #CARD# NULL NULL BT def mysql innodb_index_stats 0 mysql PRIMARY 4 stat_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 1 database_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 2 table_name A #CARD# NULL NULL BTREE -def mysql ndb_binlog_index 0 mysql PRIMARY 1 epoch A #CARD# NULL NULL BTREE def mysql plugin 0 mysql PRIMARY 1 name A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 1 db A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 2 name A #CARD# NULL NULL BTREE @@ -61,10 +60,6 @@ def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE -def mysql slave_master_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_relay_log_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 2 Worker_id A #CARD# NULL NULL BTREE def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 2 Db A #CARD# NULL NULL BTREE diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result index 3414581e969..f818db6784e 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result +++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result @@ -44,7 +44,6 @@ def mysql innodb_index_stats 0 mysql PRIMARY 3 index_name A #CARD# NULL NULL BT def mysql innodb_index_stats 0 mysql PRIMARY 4 stat_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 1 database_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 2 table_name A #CARD# NULL NULL BTREE -def mysql ndb_binlog_index 0 mysql PRIMARY 1 epoch A #CARD# NULL NULL BTREE def mysql plugin 0 mysql PRIMARY 1 name A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 1 db A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 2 name A #CARD# NULL NULL BTREE @@ -61,10 +60,6 @@ def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE -def mysql slave_master_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_relay_log_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 2 Worker_id A #CARD# NULL NULL BTREE def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 2 Db A #CARD# NULL NULL BTREE @@ -123,7 +118,6 @@ def mysql innodb_index_stats 0 mysql PRIMARY 3 index_name A #CARD# NULL NULL BT def mysql innodb_index_stats 0 mysql PRIMARY 4 stat_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 1 database_name A #CARD# NULL NULL BTREE def mysql innodb_table_stats 0 mysql PRIMARY 2 table_name A #CARD# NULL NULL BTREE -def mysql ndb_binlog_index 0 mysql PRIMARY 1 epoch A #CARD# NULL NULL BTREE def mysql plugin 0 mysql PRIMARY 1 name A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 1 db A #CARD# NULL NULL BTREE def mysql proc 0 mysql PRIMARY 2 name A #CARD# NULL NULL BTREE @@ -140,10 +134,6 @@ def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE -def mysql slave_master_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_relay_log_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 1 Master_id A #CARD# NULL NULL BTREE -def mysql slave_worker_info 0 mysql PRIMARY 2 Worker_id A #CARD# NULL NULL BTREE def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 2 Db A #CARD# NULL NULL BTREE diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints.result b/mysql-test/suite/funcs_1/r/is_table_constraints.result index fb62c16ec1e..0077f74396c 100644 --- a/mysql-test/suite/funcs_1/r/is_table_constraints.result +++ b/mysql-test/suite/funcs_1/r/is_table_constraints.result @@ -73,17 +73,12 @@ def mysql name mysql help_topic def mysql PRIMARY mysql host def mysql PRIMARY mysql index_stats def mysql PRIMARY mysql innodb_index_stats -def mysql innodb_index_stats_ibfk_1 mysql innodb_index_stats def mysql PRIMARY mysql innodb_table_stats -def mysql PRIMARY mysql ndb_binlog_index def mysql PRIMARY mysql plugin def mysql PRIMARY mysql proc def mysql PRIMARY mysql procs_priv def mysql PRIMARY mysql proxies_priv def mysql PRIMARY mysql servers -def mysql PRIMARY mysql slave_master_info -def mysql PRIMARY mysql slave_relay_log_info -def mysql PRIMARY mysql slave_worker_info def mysql PRIMARY mysql table_stats def mysql PRIMARY mysql tables_priv def mysql PRIMARY mysql time_zone diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result index 4a51fe66f1f..38f79f30068 100644 --- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result @@ -22,18 +22,13 @@ def mysql name mysql help_topic UNIQUE def mysql PRIMARY mysql help_topic PRIMARY KEY def mysql PRIMARY mysql host PRIMARY KEY def mysql PRIMARY mysql index_stats PRIMARY KEY -def mysql innodb_index_stats_ibfk_1 mysql innodb_index_stats FOREIGN KEY def mysql PRIMARY mysql innodb_index_stats PRIMARY KEY def mysql PRIMARY mysql innodb_table_stats PRIMARY KEY -def mysql PRIMARY mysql ndb_binlog_index PRIMARY KEY def mysql PRIMARY mysql plugin PRIMARY KEY def mysql PRIMARY mysql proc PRIMARY KEY def mysql PRIMARY mysql procs_priv PRIMARY KEY def mysql PRIMARY mysql proxies_priv PRIMARY KEY def mysql PRIMARY mysql servers PRIMARY KEY -def mysql PRIMARY mysql slave_master_info PRIMARY KEY -def mysql PRIMARY mysql slave_relay_log_info PRIMARY KEY -def mysql PRIMARY mysql slave_worker_info PRIMARY KEY def mysql PRIMARY mysql tables_priv PRIMARY KEY def mysql PRIMARY mysql table_stats PRIMARY KEY def mysql PRIMARY mysql time_zone PRIMARY KEY diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result index 0e1076f5b41..8195abd9657 100644 --- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result +++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result @@ -22,18 +22,13 @@ def mysql name mysql help_topic UNIQUE def mysql PRIMARY mysql help_topic PRIMARY KEY def mysql PRIMARY mysql host PRIMARY KEY def mysql PRIMARY mysql index_stats PRIMARY KEY -def mysql innodb_index_stats_ibfk_1 mysql innodb_index_stats FOREIGN KEY def mysql PRIMARY mysql innodb_index_stats PRIMARY KEY def mysql PRIMARY mysql innodb_table_stats PRIMARY KEY -def mysql PRIMARY mysql ndb_binlog_index PRIMARY KEY def mysql PRIMARY mysql plugin PRIMARY KEY def mysql PRIMARY mysql proc PRIMARY KEY def mysql PRIMARY mysql procs_priv PRIMARY KEY def mysql PRIMARY mysql proxies_priv PRIMARY KEY def mysql PRIMARY mysql servers PRIMARY KEY -def mysql PRIMARY mysql slave_master_info PRIMARY KEY -def mysql PRIMARY mysql slave_relay_log_info PRIMARY KEY -def mysql PRIMARY mysql slave_worker_info PRIMARY KEY def mysql PRIMARY mysql tables_priv PRIMARY KEY def mysql PRIMARY mysql table_stats PRIMARY KEY def mysql PRIMARY mysql time_zone PRIMARY KEY @@ -62,18 +57,13 @@ def mysql name mysql help_topic UNIQUE def mysql PRIMARY mysql help_topic PRIMARY KEY def mysql PRIMARY mysql host PRIMARY KEY def mysql PRIMARY mysql index_stats PRIMARY KEY -def mysql innodb_index_stats_ibfk_1 mysql innodb_index_stats FOREIGN KEY def mysql PRIMARY mysql innodb_index_stats PRIMARY KEY def mysql PRIMARY mysql innodb_table_stats PRIMARY KEY -def mysql PRIMARY mysql ndb_binlog_index PRIMARY KEY def mysql PRIMARY mysql plugin PRIMARY KEY def mysql PRIMARY mysql proc PRIMARY KEY def mysql PRIMARY mysql procs_priv PRIMARY KEY def mysql PRIMARY mysql proxies_priv PRIMARY KEY def mysql PRIMARY mysql servers PRIMARY KEY -def mysql PRIMARY mysql slave_master_info PRIMARY KEY -def mysql PRIMARY mysql slave_relay_log_info PRIMARY KEY -def mysql PRIMARY mysql slave_worker_info PRIMARY KEY def mysql PRIMARY mysql tables_priv PRIMARY KEY def mysql PRIMARY mysql table_stats PRIMARY KEY def mysql PRIMARY mysql time_zone PRIMARY KEY diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql.result b/mysql-test/suite/funcs_1/r/is_tables_mysql.result index 6693143e1b0..4587538ea0b 100644 --- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result @@ -359,29 +359,6 @@ user_comment Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME ndb_binlog_index -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION latin1_swedish_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME plugin TABLE_TYPE BASE TABLE ENGINE MYISAM_OR_MARIA @@ -497,75 +474,6 @@ user_comment MySQL Foreign Servers table Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME slave_master_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Master Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_relay_log_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Relay Log Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_worker_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Worker Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME slow_log TABLE_TYPE BASE TABLE ENGINE CSV diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result index 89f9255a294..1d0951c1de2 100644 --- a/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result +++ b/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result @@ -359,29 +359,6 @@ user_comment Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME ndb_binlog_index -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION latin1_swedish_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME plugin TABLE_TYPE BASE TABLE ENGINE MYISAM_OR_MARIA @@ -497,75 +474,6 @@ user_comment MySQL Foreign Servers table Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME slave_master_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Master Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_relay_log_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Relay Log Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_worker_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Worker Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME slow_log TABLE_TYPE BASE TABLE ENGINE CSV @@ -1134,29 +1042,6 @@ user_comment Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME ndb_binlog_index -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION latin1_swedish_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME plugin TABLE_TYPE BASE TABLE ENGINE MYISAM_OR_MARIA @@ -1272,75 +1157,6 @@ user_comment MySQL Foreign Servers table Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql -TABLE_NAME slave_master_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Master Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_relay_log_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Relay Log Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql -TABLE_NAME slave_worker_info -TABLE_TYPE BASE TABLE -ENGINE MYISAM_OR_MARIA -VERSION 10 -ROW_FORMAT DYNAMIC_OR_PAGE -TABLE_ROWS #TBLR# -AVG_ROW_LENGTH #ARL# -DATA_LENGTH #DL# -MAX_DATA_LENGTH #MDL# -INDEX_LENGTH #IL# -DATA_FREE #DF# -AUTO_INCREMENT NULL -CREATE_TIME #CRT# -UPDATE_TIME #UT# -CHECK_TIME #CT# -TABLE_COLLATION utf8_general_ci -CHECKSUM NULL -CREATE_OPTIONS #CO# -TABLE_COMMENT #TC# -user_comment Worker Information -Separator ----------------------------------------------------- -TABLE_CATALOG def -TABLE_SCHEMA mysql TABLE_NAME slow_log TABLE_TYPE BASE TABLE ENGINE CSV diff --git a/mysql-test/suite/funcs_1/r/is_user_privileges.result b/mysql-test/suite/funcs_1/r/is_user_privileges.result index 1ec1ffc4ce1..031067f2e64 100644 --- a/mysql-test/suite/funcs_1/r/is_user_privileges.result +++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result @@ -129,6 +129,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser2 Password @@ -171,6 +172,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser3 Password @@ -213,6 +215,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N # # Add GRANT OPTION db_datadict.* to testuser1; GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION; @@ -279,6 +282,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser2 Password @@ -321,6 +325,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser3 Password @@ -363,6 +368,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N # Establish connection testuser1 (user=testuser1) SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' @@ -415,6 +421,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser2 Password @@ -457,6 +464,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser3 Password @@ -499,6 +507,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N SHOW GRANTS; Grants for testuser1@localhost GRANT USAGE ON *.* TO 'testuser1'@'localhost' @@ -573,6 +582,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser2 Password @@ -615,6 +625,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N Host localhost User testuser3 Password @@ -657,6 +668,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +password_expired N GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION; # # Here