diff --git a/Makefile.am b/Makefile.am index dfa6c7f0f07..8a575b3c365 100644 --- a/Makefile.am +++ b/Makefile.am @@ -101,7 +101,9 @@ dist-hook: tags: support-files/build-tags -.PHONY: init-db bin-dist + +.PHONY: init-db bin-dist test test-full test-ps test-nr \ + test-ns test-pr test-unit # Target 'test' will run the regression test suite using the built server. # @@ -111,29 +113,34 @@ tags: # will then calculate the various port numbers it needs from this, # making sure each user use different ports. -test: - cd mysql-test ; \ - ./mysql-test-run.pl --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row +test-unit: + cd unittest && $(MAKE) test -test-full: +test-ps: cd mysql-test ; \ - ./mysql-test-run.pl --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --mysqld=--binlog-format=row && \ - ./mysql-test-run.pl --ps-protocol --mysqld=--binlog-format=row + ./mysql-test-run.pl $(force) --ps-protocol --mysqld=--binlog-format=statement + +test-nr: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --mysqld=--binlog-format=row + +test-pr: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --ps-protocol --mysqld=--binlog-format=row + +test-ns: + cd mysql-test ; \ + ./mysql-test-run.pl $(force) --mysqld=--binlog-format=statement + +test: test-unit test-ns test-pr + +test-full: test test-nr test-ps test-force: - cd mysql-test ; \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --ps-protocol --force --mysqld=--binlog-format=row + $(MAKE) force=--force test test-force-full: - cd mysql-test ; \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --force --ps-protocol --mysqld=--binlog-format=statement && \ - ./mysql-test-run.pl --force --mysqld=--binlog-format=row && \ - ./mysql-test-run.pl --force --ps-protocol --mysqld=--binlog-format=row + $(MAKE) force=--force test-full # Keep these for a while test-pl: test @@ -141,8 +148,6 @@ test-full-pl: test-full test-force-pl: test-force test-force-full-pl: test-force-full - - # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/config/ac-macros/plugins.m4 b/config/ac-macros/plugins.m4 index 4bf9292547d..cfc5f8dbcbe 100644 --- a/config/ac-macros/plugins.m4 +++ b/config/ac-macros/plugins.m4 @@ -406,7 +406,7 @@ dnl Although this is "pretty", it breaks libmysqld build mysql_plugin_dirs="$mysql_plugin_dirs $6" m4_syscmd(test -f "$6/configure") ifelse(m4_sysval, 0, - [other_configures="$other_configures $6/configure"], + [AC_CONFIG_SUBDIRS($6)], [AC_CONFIG_FILES($6/Makefile)] ) ifelse(m4_substr($6, 0, 8), [storage/], diff --git a/configure.in b/configure.in index be6f3c8eaec..120863dafbd 100644 --- a/configure.in +++ b/configure.in @@ -2474,10 +2474,6 @@ AC_SUBST(netware_dir) AC_SUBST(linked_netware_sources) AM_CONDITIONAL(HAVE_NETWARE, test "$netware_dir" = "netware") -# Ensure that table handlers gets all modifications to CFLAGS/CXXFLAGS -export CC CXX CFLAGS CXXFLAGS LD LDFLAGS AR -ac_configure_args="$ac_configure_args CFLAGS='$CFLAGS' CXXFLAGS='$CXXFLAGS'" - if test "$with_server" = "yes" -o "$THREAD_SAFE_CLIENT" != "no" then AC_DEFINE([THREAD], [1], @@ -2527,22 +2523,6 @@ AC_SUBST(CC) AC_SUBST(GXX) # Set configuration options for make_binary_distribution - -CONF_ARGS= -case $SYSTEM_TYPE in - *netware*) - MAKE_BINARY_DISTRIBUTION_OPTIONS=--no-strip - CONF_ARGS=--host="$MACHINE_TYPE-$SYSTEM_TYPE" - ;; - *) - MAKE_BINARY_DISTRIBUTION_OPTIONS= - ;; -esac - -for CONF in $other_configures; do - (cd `dirname $CONF`; ./`basename $CONF` $CONF_ARGS --build=$build_alias) -done - AC_SUBST(MAKE_BINARY_DISTRIBUTION_OPTIONS) # Output results @@ -2559,8 +2539,13 @@ AC_CONFIG_FILES(Makefile extra/Makefile mysys/Makefile dnl support-files/MacOSX/Makefile mysql-test/Makefile dnl mysql-test/ndb/Makefile netware/Makefile dnl include/mysql_version.h plugin/Makefile win/Makefile) - AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) - AC_OUTPUT + +AC_CONFIG_COMMANDS([default], , test -z "$CONFIG_HEADERS" || echo timestamp > stamp-h) + +# Ensure that table handlers gets all modifications to CFLAGS/CXXFLAGS +AC_CONFIG_COMMANDS_POST(ac_configure_args="$ac_configure_args CFLAGS='$CFLAGS' CXXFLAGS='$CXXFLAGS'") + +AC_OUTPUT echo echo "MySQL has a Web site at http://www.mysql.com/ which carries details on the" diff --git a/dbug/dbug.c b/dbug/dbug.c index 07f72a3e758..48b2398e9c6 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -1728,7 +1728,7 @@ static void DoPrefix(CODE_STATE *cs, uint _line_) struct tm *tm_p; if (gettimeofday(&tv, NULL) != -1) { - if ((tm_p= localtime(&tv.tv_sec))) + if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { (void) fprintf (cs->stack->out_file, /* "%04d-%02d-%02d " */ diff --git a/extra/yassl/src/handshake.cpp b/extra/yassl/src/handshake.cpp index 12b62f26e14..66ec64f4af8 100644 --- a/extra/yassl/src/handshake.cpp +++ b/extra/yassl/src/handshake.cpp @@ -880,7 +880,7 @@ int sendData(SSL& ssl, const void* buffer, int sz) ssl.SetError(no_error); ssl.verfiyHandShakeComplete(); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; int sent = 0; for (;;) { @@ -891,7 +891,7 @@ int sendData(SSL& ssl, const void* buffer, int sz) buildMessage(ssl, out, data); ssl.Send(out.get_buffer(), out.get_size()); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; sent += len; if (sent == sz) break; } @@ -918,14 +918,14 @@ int receiveData(SSL& ssl, Data& data) ssl.SetError(no_error); ssl.verfiyHandShakeComplete(); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; if (!ssl.bufferedData()) processReply(ssl); ssl.fillData(data); ssl.useLog().ShowData(data.get_length()); - if (ssl.GetError()) return 0; + if (ssl.GetError()) return -1; if (data.get_length() == 0 && ssl.getSocket().WouldBlock()) { ssl.SetError(YasslError(SSL_ERROR_WANT_READ)); diff --git a/extra/yassl/src/socket_wrapper.cpp b/extra/yassl/src/socket_wrapper.cpp index 803f4b01249..06b403c999d 100644 --- a/extra/yassl/src/socket_wrapper.cpp +++ b/extra/yassl/src/socket_wrapper.cpp @@ -113,13 +113,22 @@ uint Socket::get_ready() const uint Socket::send(const byte* buf, unsigned int sz, int flags) const { + const byte* pos = buf; + const byte* end = pos + sz; + assert(socket_ != INVALID_SOCKET); - int sent = ::send(socket_, reinterpret_cast(buf), sz, flags); + + while (pos != end) { + int sent = ::send(socket_, reinterpret_cast(pos), + static_cast(end - pos), flags); if (sent == -1) return 0; - return sent; + pos += sent; + } + + return sz; } diff --git a/include/atomic/nolock.h b/include/atomic/nolock.h index cf21a94c7de..458cb3e4f66 100644 --- a/include/atomic/nolock.h +++ b/include/atomic/nolock.h @@ -27,137 +27,9 @@ #endif #endif -#ifdef make_atomic_add_body8 +#ifdef make_atomic_add_body -#ifdef HAVE_INLINE - -#define make_atomic_add(S) \ -static inline uint ## S _my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_add_body ## S; \ - return v; \ -} - -#define make_atomic_swap(S) \ -static inline uint ## S _my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_swap_body ## S; \ - return v; \ -} - -#define make_atomic_cas(S) \ -static inline uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a,\ - uint ## S *cmp, uint ## S set) \ -{ \ - uint8 ret; \ - make_atomic_cas_body ## S; \ - return ret; \ -} - -#define make_atomic_load(S) \ -static inline uint ## S _my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a) \ -{ \ - uint ## S ret; \ - make_atomic_load_body ## S; \ - return ret; \ -} - -#define make_atomic_store(S) \ -static inline void _my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v) \ -{ \ - make_atomic_store_body ## S; \ -} - -#else /* no inline functions */ - -#define make_atomic_add(S) \ -extern uint ## S _my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#define make_atomic_swap(S) \ -extern uint ## S _my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#define make_atomic_cas(S) \ -extern uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set); - -#define make_atomic_load(S) \ -extern uint ## S _my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a); - -#define make_atomic_store(S) \ -extern void _my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v); - -#endif - -make_atomic_add( 8) -make_atomic_add(16) -make_atomic_add(32) - -make_atomic_cas( 8) -make_atomic_cas(16) -make_atomic_cas(32) - -make_atomic_load( 8) -make_atomic_load(16) -make_atomic_load(32) - -make_atomic_store( 8) -make_atomic_store(16) -make_atomic_store(32) - -make_atomic_swap( 8) -make_atomic_swap(16) -make_atomic_swap(32) - -#undef make_atomic_add_body8 -#undef make_atomic_cas_body8 -#undef make_atomic_load_body8 -#undef make_atomic_store_body8 -#undef make_atomic_swap_body8 -#undef make_atomic_add_body16 -#undef make_atomic_cas_body16 -#undef make_atomic_load_body16 -#undef make_atomic_store_body16 -#undef make_atomic_swap_body16 -#undef make_atomic_add_body32 -#undef make_atomic_cas_body32 -#undef make_atomic_load_body32 -#undef make_atomic_store_body32 -#undef make_atomic_swap_body32 -#undef make_atomic_add -#undef make_atomic_cas -#undef make_atomic_load -#undef make_atomic_store -#undef make_atomic_swap - -#define my_atomic_add8(a,v,L) _my_atomic_add8(a,v) -#define my_atomic_add16(a,v,L) _my_atomic_add16(a,v) -#define my_atomic_add32(a,v,L) _my_atomic_add32(a,v) - -#define my_atomic_cas8(a,c,v,L) _my_atomic_cas8(a,c,v) -#define my_atomic_cas16(a,c,v,L) _my_atomic_cas16(a,c,v) -#define my_atomic_cas32(a,c,v,L) _my_atomic_cas32(a,c,v) - -#define my_atomic_load8(a,L) _my_atomic_load8(a) -#define my_atomic_load16(a,L) _my_atomic_load16(a) -#define my_atomic_load32(a,L) _my_atomic_load32(a) - -#define my_atomic_store8(a,v,L) _my_atomic_store8(a,v) -#define my_atomic_store16(a,v,L) _my_atomic_store16(a,v) -#define my_atomic_store32(a,v,L) _my_atomic_store32(a,v) - -#define my_atomic_swap8(a,v,L) _my_atomic_swap8(a,v) -#define my_atomic_swap16(a,v,L) _my_atomic_swap16(a,v) -#define my_atomic_swap32(a,v,L) _my_atomic_swap32(a,v) - -#define my_atomic_rwlock_t typedef int +typedef struct { } my_atomic_rwlock_t; #define my_atomic_rwlock_destroy(name) #define my_atomic_rwlock_init(name) #define my_atomic_rwlock_rdlock(name) diff --git a/include/atomic/rwlock.h b/include/atomic/rwlock.h index ca5be29ab9b..12d0dd3f069 100644 --- a/include/atomic/rwlock.h +++ b/include/atomic/rwlock.h @@ -16,12 +16,6 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; -#ifdef MY_ATOMIC_EXTRA_DEBUG -#define CHECK_RW if (rw) if (a->rw) assert(rw == a->rw); else a->rw=rw; -#else -#define CHECK_RW -#endif - #ifdef MY_ATOMIC_MODE_DUMMY /* the following can never be enabled by ./configure, one need to put #define in @@ -36,6 +30,7 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; #define my_atomic_rwlock_wrlock(name) #define my_atomic_rwlock_rdunlock(name) #define my_atomic_rwlock_wrunlock(name) +#define MY_ATOMIC_MODE "dummy (non-atomic)" #else #define my_atomic_rwlock_destroy(name) pthread_rwlock_destroy(& (name)->rw) #define my_atomic_rwlock_init(name) pthread_rwlock_init(& (name)->rw, 0) @@ -43,119 +38,12 @@ typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; #define my_atomic_rwlock_wrlock(name) pthread_rwlock_wrlock(& (name)->rw) #define my_atomic_rwlock_rdunlock(name) pthread_rwlock_unlock(& (name)->rw) #define my_atomic_rwlock_wrunlock(name) pthread_rwlock_unlock(& (name)->rw) +#define MY_ATOMIC_MODE "rwlocks" #endif -#ifdef HAVE_INLINE - -#define make_atomic_add(S) \ -static inline uint ## S my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - a->val+= v; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_swap(S) \ -static inline uint ## S my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - a->val= v; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_cas(S) \ -static inline uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw) \ -{ \ - uint ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - if (ret= (a->val == *cmp)) a->val= set; else *cmp=a->val; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_load(S) \ -static inline uint ## S my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw) \ -{ \ - uint ## S ret; \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_wrlock(rw); \ - ret= a->val; \ - if (rw) my_atomic_rwlock_wrunlock(rw); \ - return ret; \ -} - -#define make_atomic_store(S) \ -static inline void my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ -{ \ - CHECK_RW; \ - if (rw) my_atomic_rwlock_rdlock(rw); \ - (a)->val= (v); \ - if (rw) my_atomic_rwlock_rdunlock(rw); \ -} - -#else /* no inline functions */ - -#define make_atomic_add(S) \ -extern uint ## S my_atomic_add ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#define make_atomic_swap(S) \ -extern uint ## S my_atomic_swap ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#define make_atomic_cas(S) \ -extern uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ - uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw); - -#define make_atomic_load(S) \ -extern uint ## S my_atomic_load ## S( \ - my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw); - -#define make_atomic_store(S) \ -extern void my_atomic_store ## S( \ - my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); - -#endif - -make_atomic_add( 8) -make_atomic_add(16) -make_atomic_add(32) -make_atomic_add(64) -make_atomic_cas( 8) -make_atomic_cas(16) -make_atomic_cas(32) -make_atomic_cas(64) -make_atomic_load( 8) -make_atomic_load(16) -make_atomic_load(32) -make_atomic_load(64) -make_atomic_store( 8) -make_atomic_store(16) -make_atomic_store(32) -make_atomic_store(64) -make_atomic_swap( 8) -make_atomic_swap(16) -make_atomic_swap(32) -make_atomic_swap(64) -#undef make_atomic_add -#undef make_atomic_cas -#undef make_atomic_load -#undef make_atomic_store -#undef make_atomic_swap -#undef CHECK_RW - +#define make_atomic_add_body(S) int ## S sav; sav= *a; *a+= v; v=sav; +#define make_atomic_swap_body(S) int ## S sav; sav= *a; *a= v; v=sav; +#define make_atomic_cas_body(S) if ((ret= (*a == *cmp))) *a= set; else *cmp=*a; +#define make_atomic_load_body(S) ret= *a; +#define make_atomic_store_body(S) *a= v; diff --git a/include/atomic/x86-gcc.h b/include/atomic/x86-gcc.h index 7576db54d69..df6fcdc5ad2 100644 --- a/include/atomic/x86-gcc.h +++ b/include/atomic/x86-gcc.h @@ -16,44 +16,38 @@ /* XXX 64-bit atomic operations can be implemented using - cmpxchg8b, if necessary + cmpxchg8b, if necessary. Though I've heard that not all 64-bit + architectures support double-word (128-bit) cas. */ -/* fix -ansi errors while maintaining readability */ -#define asm __asm__ +#define MY_ATOMIC_MODE "gcc-x86" ## LOCK -#define make_atomic_add_body8 \ - asm volatile (LOCK "xadd %0, %1;" : "+r" (v) , "+m" (a->val)) -#define make_atomic_swap_body8 \ - asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (a->val)) -#define make_atomic_cas_body8 \ +/* fix -ansi errors while maintaining readability */ +#ifndef asm +#define asm __asm__ +#endif + +#define make_atomic_add_body(S) \ + asm volatile (LOCK "xadd %0, %1;" : "+r" (v) , "+m" (*a)) +#define make_atomic_swap_body(S) \ + asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (*a)) +#define make_atomic_cas_body(S) \ asm volatile (LOCK "cmpxchg %3, %0; setz %2;" \ - : "+m" (a->val), "+a" (*cmp), "=q" (ret): "r" (set)) + : "+m" (*a), "+a" (*cmp), "=q" (ret): "r" (set)) #ifdef MY_ATOMIC_MODE_DUMMY -#define make_atomic_load_body8 ret=a->val -#define make_atomic_store_body8 a->val=v +#define make_atomic_load_body(S) ret=*a +#define make_atomic_store_body(S) *a=v #else /* Actually 32-bit reads/writes are always atomic on x86 But we add LOCK here anyway to force memory barriers */ -#define make_atomic_load_body8 \ +#define make_atomic_load_body(S) \ ret=0; \ asm volatile (LOCK "cmpxchg %2, %0" \ - : "+m" (a->val), "+a" (ret): "r" (ret)) -#define make_atomic_store_body8 \ - asm volatile ("xchg %0, %1;" : "+m" (a->val) : "r" (v)) + : "+m" (*a), "+a" (ret): "r" (ret)) +#define make_atomic_store_body(S) \ + asm volatile ("xchg %0, %1;" : "+m" (*a) : "r" (v)) #endif -#define make_atomic_add_body16 make_atomic_add_body8 -#define make_atomic_add_body32 make_atomic_add_body8 -#define make_atomic_cas_body16 make_atomic_cas_body8 -#define make_atomic_cas_body32 make_atomic_cas_body8 -#define make_atomic_load_body16 make_atomic_load_body8 -#define make_atomic_load_body32 make_atomic_load_body8 -#define make_atomic_store_body16 make_atomic_store_body8 -#define make_atomic_store_body32 make_atomic_store_body8 -#define make_atomic_swap_body16 make_atomic_swap_body8 -#define make_atomic_swap_body32 make_atomic_swap_body8 - diff --git a/include/atomic/x86-msvc.h b/include/atomic/x86-msvc.h index 19645551196..a7e14d0e926 100644 --- a/include/atomic/x86-msvc.h +++ b/include/atomic/x86-msvc.h @@ -23,63 +23,75 @@ // (InterlockedCompareExchange, InterlockedCompareExchange16 // InterlockedExchangeAdd, InterlockedExchange) -#define make_atomic_add_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm LOCK xadd a->val, REG \ - _asm movzx v, REG \ +#ifndef _atomic_h_cleanup_ +#define _atomic_h_cleanup_ "atomic/x86-msvc.h" + +#define MY_ATOMIC_MODE "msvc-x86" ## LOCK + +#define make_atomic_add_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm LOCK xadd *a, reg_ ## S \ + _asm movzx v, reg_ ## S \ } -#define make_atomic_cas_body(AREG,REG2) \ - _asm { \ - _asm mov AREG, *cmp \ - _asm mov REG2, set \ - _asm LOCK cmpxchg a->val, REG2 \ - _asm mov *cmp, AREG \ - _asm setz al \ - _asm movzx ret, al \ +#define make_atomic_cas_body(S) \ + _asm { \ + _asm mov areg_ ## S, *cmp \ + _asm mov reg2_ ## S, set \ + _asm LOCK cmpxchg *a, reg2_ ## S \ + _asm mov *cmp, areg_ ## S \ + _asm setz al \ + _asm movzx ret, al \ } -#define make_atomic_swap_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm xchg a->val, REG \ - _asm mov v, REG \ +#define make_atomic_swap_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm xchg *a, reg_ ## S \ + _asm mov v, reg_ ## S \ } #ifdef MY_ATOMIC_MODE_DUMMY -#define make_atomic_load_body(AREG,REG) ret=a->val -#define make_atomic_store_body(REG) a->val=v +#define make_atomic_load_body(S) ret=*a +#define make_atomic_store_body(S) *a=v #else /* Actually 32-bit reads/writes are always atomic on x86 But we add LOCK here anyway to force memory barriers */ -#define make_atomic_load_body(AREG,REG2) \ - _asm { \ - _asm mov AREG, 0 \ - _asm mov REG2, AREG \ - _asm LOCK cmpxchg a->val, REG2 \ - _asm mov ret, AREG \ +#define make_atomic_load_body(S) \ + _asm { \ + _asm mov areg_ ## S, 0 \ + _asm mov reg2_ ## S, areg_ ## S \ + _asm LOCK cmpxchg *a, reg2_ ## S \ + _asm mov ret, areg_ ## S \ } -#define make_atomic_store_body(REG) \ - _asm { \ - _asm mov REG, v \ - _asm xchg a->val, REG \ +#define make_atomic_store_body(S) \ + _asm { \ + _asm mov reg_ ## S, v \ + _asm xchg *a, reg_ ## S \ } #endif -#define make_atomic_add_body8 make_atomic_add_body(al) -#define make_atomic_add_body16 make_atomic_add_body(ax) -#define make_atomic_add_body32 make_atomic_add_body(eax) -#define make_atomic_cas_body8 make_atomic_cas_body(al, bl) -#define make_atomic_cas_body16 make_atomic_cas_body(ax, bx) -#define make_atomic_cas_body32 make_atomic_cas_body(eax, ebx) -#define make_atomic_load_body8 make_atomic_load_body(al, bl) -#define make_atomic_load_body16 make_atomic_load_body(ax, bx) -#define make_atomic_load_body32 make_atomic_load_body(eax, ebx) -#define make_atomic_store_body8 make_atomic_store_body(al) -#define make_atomic_store_body16 make_atomic_store_body(ax) -#define make_atomic_store_body32 make_atomic_store_body(eax) -#define make_atomic_swap_body8 make_atomic_swap_body(al) -#define make_atomic_swap_body16 make_atomic_swap_body(ax) -#define make_atomic_swap_body32 make_atomic_swap_body(eax) +#define reg_8 al +#define reg_16 ax +#define reg_32 eax +#define areg_8 al +#define areg_16 ax +#define areg_32 eax +#define reg2_8 bl +#define reg2_16 bx +#define reg2_32 ebx + +#else /* cleanup */ + +#undef reg_8 +#undef reg_16 +#undef reg_32 +#undef areg_8 +#undef areg_16 +#undef areg_32 +#undef reg2_8 +#undef reg2_16 +#undef reg2_32 +#endif diff --git a/include/my_atomic.h b/include/my_atomic.h index 091edc0f57b..877443fe705 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -14,21 +14,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef atomic_rwlock_init +#ifndef my_atomic_rwlock_init -#ifdef MY_ATOMIC_EXTRA_DEBUG -#ifndef MY_ATOMIC_MODE_RWLOCKS -#error MY_ATOMIC_EXTRA_DEBUG can be only used with MY_ATOMIC_MODE_RWLOCKS -#endif -#define LOCK_PTR void *rw; -#else -#define LOCK_PTR -#endif - -typedef volatile struct {uint8 val; LOCK_PTR} my_atomic_8_t; -typedef volatile struct {uint16 val; LOCK_PTR} my_atomic_16_t; -typedef volatile struct {uint32 val; LOCK_PTR} my_atomic_32_t; -typedef volatile struct {uint64 val; LOCK_PTR} my_atomic_64_t; +#define intptr void * #ifndef MY_ATOMIC_MODE_RWLOCKS #include "atomic/nolock.h" @@ -38,6 +26,103 @@ typedef volatile struct {uint64 val; LOCK_PTR} my_atomic_64_t; #include "atomic/rwlock.h" #endif +#ifdef HAVE_INLINE + +#define make_atomic_add(S) \ +static inline int ## S my_atomic_add ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_add_body(S); \ + return v; \ +} + +#define make_atomic_swap(S) \ +static inline int ## S my_atomic_swap ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_swap_body(S); \ + return v; \ +} + +#define make_atomic_cas(S) \ +static inline int my_atomic_cas ## S(int ## S volatile *a, \ + int ## S *cmp, int ## S set) \ +{ \ + int8 ret; \ + make_atomic_cas_body(S); \ + return ret; \ +} + +#define make_atomic_load(S) \ +static inline int ## S my_atomic_load ## S(int ## S volatile *a) \ +{ \ + int ## S ret; \ + make_atomic_load_body(S); \ + return ret; \ +} + +#define make_atomic_store(S) \ +static inline void my_atomic_store ## S( \ + int ## S volatile *a, int ## S v) \ +{ \ + make_atomic_store_body(S); \ +} + +#else /* no inline functions */ + +#define make_atomic_add(S) \ +extern int ## S my_atomic_add ## S(int ## S volatile *a, int ## S v); + +#define make_atomic_swap(S) \ +extern int ## S my_atomic_swap ## S(int ## S volatile *a, int ## S v); + +#define make_atomic_cas(S) \ +extern int my_atomic_cas ## S(int ## S volatile *a, int ## S *cmp, int ## S set); + +#define make_atomic_load(S) \ +extern int ## S my_atomic_load ## S(int ## S volatile *a); + +#define make_atomic_store(S) \ +extern void my_atomic_store ## S(int ## S volatile *a, int ## S v); + +#endif + +make_atomic_add( 8) +make_atomic_add(16) +make_atomic_add(32) + +make_atomic_cas( 8) +make_atomic_cas(16) +make_atomic_cas(32) +make_atomic_cas(ptr) + +make_atomic_load( 8) +make_atomic_load(16) +make_atomic_load(32) +make_atomic_load(ptr) + +make_atomic_store( 8) +make_atomic_store(16) +make_atomic_store(32) +make_atomic_store(ptr) + +make_atomic_swap( 8) +make_atomic_swap(16) +make_atomic_swap(32) +make_atomic_swap(ptr) + +#undef make_atomic_add +#undef make_atomic_cas +#undef make_atomic_load +#undef make_atomic_store +#undef make_atomic_swap +#undef intaptr + +#ifdef _atomic_h_cleanup_ +#include _atomic_h_cleanup_ +#undef _atomic_h_cleanup_ +#endif + #define MY_ATOMIC_OK 0 #define MY_ATOMIC_NOT_1CPU 1 extern int my_atomic_initialize(); diff --git a/mysql-test/extra/rpl_tests/rpl_log.test b/mysql-test/extra/rpl_tests/rpl_log.test index 20ee7cd0d8f..cfbc8110d06 100644 --- a/mysql-test/extra/rpl_tests/rpl_log.test +++ b/mysql-test/extra/rpl_tests/rpl_log.test @@ -130,3 +130,21 @@ show binlog events in 'slave-bin.000005' from 4; DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; + +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +--replace_result $VERSION VERSION +--replace_column 2 # 5 # +show binlog events; +select * from t1; +drop table t1; + +# End of 4.1 tests + diff --git a/mysql-test/r/archive.result b/mysql-test/r/archive.result index 19cb0c9768f..cacf4aaf304 100644 --- a/mysql-test/r/archive.result +++ b/mysql-test/r/archive.result @@ -6254,6 +6254,7 @@ auto fld1 companynr fld3 fld4 fld5 fld6 3 011402 37 Romans scholastics jarring 4 011403 37 intercepted audiology tinily DELETE FROM t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat @@ -8685,6 +8686,7 @@ auto fld1 companynr fld3 fld4 fld5 fld6 3 011402 37 Romans scholastics jarring 4 011403 37 intercepted audiology tinily TRUNCATE TABLE t2; +ERROR HY000: Table storage engine for 't2' doesn't have this option SELECT * FROM t2; auto fld1 companynr fld3 fld4 fld5 fld6 1 000001 00 Omaha teethe neat diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index d9e9392f618..10f26c40553 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -378,6 +378,28 @@ t1 CREATE TABLE `t1` ( KEY `t1_name` (`t1_name`) ) ENGINE=MyISAM AUTO_INCREMENT=1003 DEFAULT CHARSET=latin1 DROP TABLE `t1`; +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +a t1a +1 1 +2 1 +3 2 +4 2 +5 2 +6 3 +7 3 +8 3 +9 3 +drop table t1, t2; End of 4.1 tests CREATE TABLE t1 ( `a` int(11) NOT NULL auto_increment, `b` int(11) default NULL,PRIMARY KEY (`a`),UNIQUE KEY `b` (`b`)); insert into t1 (b) values (1); diff --git a/mysql-test/r/csv.result b/mysql-test/r/csv.result index 3adcc895474..04f0636d400 100644 --- a/mysql-test/r/csv.result +++ b/mysql-test/r/csv.result @@ -4944,10 +4944,10 @@ val UPDATE bug13894 SET val=6 WHERE val=10; SELECT * FROM bug13894; val +6 +6 5 11 -6 -6 DROP TABLE bug13894; DROP TABLE IF EXISTS bug14672; CREATE TABLE bug14672 (c1 integer) engine = CSV; diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 1246c6f3d5d..b092a21787f 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -1112,6 +1112,46 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK drop table t1; +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, +a varchar(16) collate utf8_unicode_ci NOT NULL default '', +b int, +f varchar(128) default 'XXX', +INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES +(1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), +(4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), +(7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), +(10, 'eeeee', 40), (11, 'bbbbbb', 60); +SELECT id, a, b FROM t1; +id a b +1 cccc 50 +2 cccc 70 +3 cccc 30 +4 cccc 30 +5 cccc 20 +6 bbbbbb 40 +7 dddd 30 +8 aaaa 10 +9 aaaa 50 +10 eeeee 40 +11 bbbbbb 60 +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; +id a b +8 aaaa 10 +9 aaaa 50 +6 bbbbbb 40 +11 bbbbbb 60 +SELECT id, a FROM t1 WHERE a='bbbbbb'; +id a +6 bbbbbb +11 bbbbbb +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; +id a +6 bbbbbb +11 bbbbbb +DROP TABLE t1; CREATE TABLE t1(id varchar(20) NOT NULL) DEFAULT CHARSET=utf8; INSERT INTO t1 VALUES ('xxx'), ('aa'), ('yyy'), ('aa'); SELECT id FROM t1; diff --git a/mysql-test/r/delayed.result b/mysql-test/r/delayed.result index f8ae61b03fb..a336f3b4108 100644 --- a/mysql-test/r/delayed.result +++ b/mysql-test/r/delayed.result @@ -39,3 +39,33 @@ select * from t1; a 1 drop table t1; +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); +delete from t1 where a=6; +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +insert delayed into t1 values(null); +select * from t1 order by a; +a +1 +2 +3 +4 +5 +7 +8 +9 +10 +11 +12 +13 +DROP TABLE t1; diff --git a/mysql-test/r/fulltext_left_join.result b/mysql-test/r/fulltext_left_join.result index f3dad290525..68a424fa3a5 100644 --- a/mysql-test/r/fulltext_left_join.result +++ b/mysql-test/r/fulltext_left_join.result @@ -50,3 +50,20 @@ venue_id venue_text dt name entity_id 1 a1 2003-05-23 19:30:00 aberdeen town hall 1 NULL a2 2003-05-23 19:30:00 NULL NULL drop table t1,t2; +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +id d e +2 aword bword +4 bword aword +5 aword and bword NULL +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +id d e m_id f +1 aword NULL 1 bword +2 aword bword NULL NULL +3 bword NULL 3 aword +4 bword aword NULL NULL +5 aword and bword NULL 5 +drop table t1,t2; diff --git a/mysql-test/r/func_concat.result b/mysql-test/r/func_concat.result index 0bd53b32dd7..66808afd4e9 100644 --- a/mysql-test/r/func_concat.result +++ b/mysql-test/r/func_concat.result @@ -68,3 +68,17 @@ select 'a' union select concat('a', -0.0000); a a a0.0000 +select concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) +as t3; +concat((select x from (select 'a' as x) as t1 ), +(select y from (select 'b' as y) as t2 )) +ab +ab +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +a +1234562 +x +drop table t1; diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 824edbbe3a6..d9695c5f3df 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -821,6 +821,41 @@ SELECT MAX(id) FROM t1 WHERE id < 3 AND a=2 AND b=6; MAX(id) NULL DROP TABLE t1; +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; +id b +1 xx +2 aa +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +MAX(b) +aa +SHOW WARNINGS; +Level Code Message +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +MAX(b) +aa +DROP TABLE t1; +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +MAX(b) +xxxxbbbb +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +DROP TABLE t1; +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +INSERT INTO t1 VALUES +(1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), +(1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); +SELECT MAX(b) FROM t1; +MAX(b) +__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________zz +EXPLAIN SELECT MAX(b) FROM t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 4 +DROP TABLE t1; create table t2 (ff double); insert into t2 values (2.2); select cast(sum(distinct ff) as decimal(5,2)) from t2; diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result index e38e2624e19..f758e2adfd9 100644 --- a/mysql-test/r/func_in.result +++ b/mysql-test/r/func_in.result @@ -202,6 +202,35 @@ select count(*) from t1 where id not in (1,2); count(*) 1 drop table t1; +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +f1 +1 +z +select f2 from t1 where f2 in (1,'z'); +f2 +0 +1 +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'z' +select f1 from t1 where 'z' in (1,f1); +f1 +z +select * from t1 where 'z' in (f2,f1); +f1 f2 +1 0 +a 1 +z 2 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' +Warning 1292 Truncated incorrect DOUBLE value: 'z' +select * from t1 where 1 in (f2,f1); +f1 f2 +1 0 +a 1 +drop table t1; CREATE TABLE t1 (a int PRIMARY KEY); INSERT INTO t1 VALUES (44), (45), (46); SELECT * FROM t1 WHERE a IN (45); diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 354c886b19b..54960765c56 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1023,6 +1023,21 @@ NULL select ifnull(load_file("lkjlkj"),"it's null"); ifnull(load_file("lkjlkj"),"it's null") it's null +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +f1 f2 +test 098f6bcd4621d373cade4e832627b4f6 +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +f1 f2 +test a94a8fe5ccb19ba61c4c0873d391e987982fbbd3 +drop table t1; End of 4.1 tests create table t1 (d decimal default null); insert into t1 values (null); diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index d54989e27bc..43c9748bace 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -751,6 +751,49 @@ select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m')) NULL NULL January NULL +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +f1 +2006-01-01 +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +f1 +2006-01-01 +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +f2 +12:01:02 +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +f3 +2006-01-01 12:01:01 +select f1 from t1 where "2006-1-1" between f1 and f3; +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +f1 +Warnings: +Warning 1292 Incorrect date value: 'zzz' for column 'f1' at row 1 +Warning 1292 Truncated incorrect INTEGER value: 'zzz' +Warning 1292 Truncated incorrect INTEGER value: 'zzz' +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +f1 +2006-01-01 +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +f1 +2006-01-02 +drop table t1; select now() - now() + 0, curtime() - curtime() + 0, sec_to_time(1) + 0, from_unixtime(1) + 0; now() - now() + 0 curtime() - curtime() + 0 sec_to_time(1) + 0 from_unixtime(1) + 0 diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result index ce491505030..8ad6f344c4f 100644 --- a/mysql-test/r/join.result +++ b/mysql-test/r/join.result @@ -744,3 +744,23 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; a2 1 drop table t1,t2,t3,t4; +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; +y c b a z +1 3 2 1 4 +select * from ((t3 natural join (t1 natural join t2)) natural join t4) +natural join t5; +y c b a z +1 3 2 1 4 +drop table t1, t2, t3, t4, t5; diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index 6fdd105fd6c..8791b8cc080 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -476,6 +476,11 @@ aclid bigint, index idx_acl(aclid) insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +create table t1(a int); +create table t2(a int); +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +ERROR HY000: You can't specify target table 't1' for update in FROM clause +drop table t1, t2; create table t1 ( c char(8) not null ) engine=innodb; insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'); insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F'); diff --git a/mysql-test/r/ndb_condition_pushdown.result b/mysql-test/r/ndb_condition_pushdown.result index 140324a21d8..2fc1e8f3127 100644 --- a/mysql-test/r/ndb_condition_pushdown.result +++ b/mysql-test/r/ndb_condition_pushdown.result @@ -1842,5 +1842,29 @@ a b select * from t1 where b like 'abc' or b like 'abc'; a b 3 abc +drop table t1; +create table t1 ( fname varchar(255), lname varchar(255) ) +engine=ndbcluster; +insert into t1 values ("Young","Foo"); +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +insert into t1 values ("aaa", "aaa"); +insert into t1 values ("bbb", "bbb"); +insert into t1 values ("ccc", "ccc"); +insert into t1 values ("ddd", "ddd"); +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +fname lname +Young Foo set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4,t5; diff --git a/mysql-test/r/replace.result b/mysql-test/r/replace.result index ca32b3d45bf..842302c89ac 100644 --- a/mysql-test/r/replace.result +++ b/mysql-test/r/replace.result @@ -24,3 +24,9 @@ a b 63 default_value 127 last drop table t1; +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +REPLACE INTO v1 (f1) VALUES (1); +ERROR HY000: CHECK OPTION failed 'test.v1' +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/r/rpl_stm_log.result b/mysql-test/r/rpl_stm_log.result index 02a861ceb53..e0b1aa12c9b 100644 --- a/mysql-test/r/rpl_stm_log.result +++ b/mysql-test/r/rpl_stm_log.result @@ -98,3 +98,20 @@ ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find tar DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; +create table t1(a int auto_increment primary key, b int); +insert into t1 values (NULL, 1); +reset master; +set insert_id=5; +insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Format_desc 2 # Server ver: VERSION, Binlog ver: 4 +slave-bin.000001 # Intvar 2 # LAST_INSERT_ID=1 +slave-bin.000001 # Intvar 2 # INSERT_ID=5 +slave-bin.000001 # Query 2 # use `test`; insert into t1 values (NULL, last_insert_id()), (NULL, last_insert_id()) +select * from t1; +a b +1 1 +5 1 +6 1 +drop table t1; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index d47d49b5298..7e54acfae2a 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2656,16 +2656,6 @@ t11 MyISAM 10 Dynamic 0 0 X X X X X X X X latin1_swedish_ci NULL select 123 as a from t1 where f1 is null; a drop table t1,t11; -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -a c -(SELECT a, b AS c FROM t1) ORDER BY b+1; -a c -SELECT a, b AS c FROM t1 ORDER BY c+1; -a c -SELECT a, b AS c FROM t1 ORDER BY b+1; -a c -drop table t1; CREATE TABLE t1 ( a INT NOT NULL, b INT NOT NULL, UNIQUE idx (a,b) ); INSERT INTO t1 VALUES (1,1),(1,2),(1,3),(1,4); CREATE TABLE t2 ( a INT NOT NULL, b INT NOT NULL, e INT ); @@ -2716,6 +2706,20 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 +where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and +t2.b like '%%' order by t2.b limit 0,1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref b,c b 5 const 1 Using where; Using temporary; Using filesort +1 SIMPLE t3 index PRIMARY,a,b PRIMARY 8 NULL 2 Using index +1 SIMPLE t2 ALL PRIMARY NULL NULL NULL 2 Range checked for each record (index map: 0x1) +DROP TABLE t1,t2,t3; CREATE TABLE t1 (a int, INDEX idx(a)); INSERT INTO t1 VALUES (2), (3), (1); EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); @@ -2726,61 +2730,6 @@ ERROR 42000: Key 'a' doesn't exist in table 't1' EXPLAIN SELECT * FROM t1 FORCE INDEX (a); ERROR 42000: Key 'a' doesn't exist in table 't1' DROP TABLE t1; -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); -SELECT * FROM t1 WHERE city='London'; -city -London -SELECT * FROM t1 WHERE city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city='London' AND city='london'; -city -London -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -city -London -DROP TABLE t1; -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -a-b -0 -1 -18446744073709551615 -select a-b , (a-b < 0) from t1 order by 1; -a-b (a-b < 0) -0 0 -1 0 -18446744073709551615 0 -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -d (a-b >= 0) b -1 1 0 -0 1 1 -18446744073709551615 1 2 -select cast((a - b) as unsigned) from t1 order by 1; -cast((a - b) as unsigned) -0 -1 -18446744073709551615 -drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; CREATE TABLE t1 ( K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', @@ -2814,19 +2763,6 @@ WART 0100 1 WART 0200 1 WART 0300 3 DROP TABLE t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; create table t1 (a int, b int); create table t2 like t1; select t1.a from (t1 inner join t2 on t1.a=t2.a) where t2.a=1; @@ -2857,29 +2793,6 @@ x NULL 1.0000 drop table t1; -create table t1 (a int(11)); -select all all * from t1; -a -select distinct distinct * from t1; -a -select all distinct * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -select distinct all * from t1; -ERROR HY000: Incorrect usage of ALL and DISTINCT -drop table t1; -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 5 -1 SIMPLE t2 ref a a 23 test.t1.a 2 -DROP TABLE t1, t2; CREATE TABLE t1 (a int); CREATE TABLE t2 (a int); INSERT INTO t1 VALUES (1), (2), (3), (4), (5); @@ -3456,3 +3369,23 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using where 1 SIMPLE t2 ref c c 5 test.t1.a 2 Using where DROP TABLE t1, t2; +create table t1 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c bit not null +); +create table t2 ( +a int unsigned not null auto_increment primary key, +b bit not null, +c int unsigned not null, +d varchar(50) +); +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; +a t1.b + 0 t1.c + 0 a t2.b + 0 c d +1 0 1 1 0 1 NULL +2 0 1 NULL NULL NULL NULL +drop table t1,t2; diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result index 702b589c911..b15683a0882 100644 --- a/mysql-test/r/type_newdecimal.result +++ b/mysql-test/r/type_newdecimal.result @@ -1397,3 +1397,14 @@ c1 9999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999 drop table t1; +create table t1 (i int, j int); +insert into t1 values (1,1), (1,2), (2,3), (2,4); +select i, count(distinct j) from t1 group by i; +i count(distinct j) +1 2 +2 2 +select i+0.0 as i2, count(distinct j) from t1 group by i2; +i2 count(distinct j) +1.0 2 +2.0 2 +drop table t1; diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 57beb6e4197..4280ae28e3c 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1306,3 +1306,48 @@ id 5 99 drop table t1; +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); +avg(1) +NULL diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index db82016f398..ad6028b8a8c 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -620,3 +620,32 @@ ERROR HY000: There is no 'no-such-user'@'localhost' registered DROP VIEW v; DROP TABLE t1; USE test; +CREATE USER mysqltest_db1@localhost identified by 'PWD'; +GRANT ALL ON mysqltest_db1.* TO mysqltest_db1@localhost WITH GRANT OPTION; +CREATE SCHEMA mysqltest_db1 ; +USE mysqltest_db1 ; +CREATE TABLE t1 (f1 INTEGER); +CREATE VIEW view1 AS +SELECT * FROM t1; +SHOW CREATE VIEW view1; +View Create View +view1 CREATE ALGORITHM=UNDEFINED DEFINER=`mysqltest_db1`@`localhost` SQL SECURITY DEFINER VIEW `view1` AS select `t1`.`f1` AS `f1` from `t1` +CREATE VIEW view2 AS +SELECT * FROM view1; +# Here comes a suspicious warning +SHOW CREATE VIEW view2; +View Create View +view2 CREATE ALGORITHM=UNDEFINED DEFINER=`mysqltest_db1`@`localhost` SQL SECURITY DEFINER VIEW `view2` AS select `view1`.`f1` AS `f1` from `view1` +# But the view view2 is usable +SELECT * FROM view2; +f1 +CREATE VIEW view3 AS +SELECT * FROM view2; +SELECT * from view3; +f1 +DROP VIEW mysqltest_db1.view3; +DROP VIEW mysqltest_db1.view2; +DROP VIEW mysqltest_db1.view1; +DROP TABLE mysqltest_db1.t1; +DROP SCHEMA mysqltest_db1; +DROP USER mysqltest_db1@localhost; diff --git a/mysql-test/t/archive.test b/mysql-test/t/archive.test index c89d31c69b9..ae8aa8d4f85 100644 --- a/mysql-test/t/archive.test +++ b/mysql-test/t/archive.test @@ -1330,12 +1330,14 @@ SELECT * FROM t2; # # For bug #12836 # Delete was allowing all rows to be removed +--error 1031 DELETE FROM t2; SELECT * FROM t2; INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W'); INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring',''); INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily',''); SELECT * FROM t2; +--error 1031 TRUNCATE TABLE t2; SELECT * FROM t2; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index e0b024d021b..2674639d0ac 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -238,6 +238,23 @@ SHOW CREATE TABLE `t1`; DROP TABLE `t1`; +# +# Bug #6880: LAST_INSERT_ID() within a statement +# + +create table t1(a int not null auto_increment primary key); +create table t2(a int not null auto_increment primary key, t1a int); +insert into t1 values(NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()); +insert into t1 values (NULL); +insert into t2 values (NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()), +(NULL, LAST_INSERT_ID()), (NULL, LAST_INSERT_ID()); +select * from t2; +drop table t1, t2; + --echo End of 4.1 tests # diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index 5ca1f58d233..b58a2cf97d4 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -912,6 +912,32 @@ INSERT INTO t1 VALUES('uUABCDEFGHIGKLMNOPRSTUVWXYZ̈bbbbbbbbbbbbbbbbbbbbbbbbbbbb check table t1; drop table t1; +# +# Bug#14896: Comparison with a key in a partial index over mb chararacter field +# + +SET NAMES utf8; +CREATE TABLE t1 (id int PRIMARY KEY, + a varchar(16) collate utf8_unicode_ci NOT NULL default '', + b int, + f varchar(128) default 'XXX', + INDEX (a(4)) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +INSERT INTO t1(id, a, b) VALUES + (1, 'cccc', 50), (2, 'cccc', 70), (3, 'cccc', 30), + (4, 'cccc', 30), (5, 'cccc', 20), (6, 'bbbbbb', 40), + (7, 'dddd', 30), (8, 'aaaa', 10), (9, 'aaaa', 50), + (10, 'eeeee', 40), (11, 'bbbbbb', 60); + +SELECT id, a, b FROM t1; + +SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb'; + +SELECT id, a FROM t1 WHERE a='bbbbbb'; +SELECT id, a FROM t1 WHERE a='bbbbbb' ORDER BY b; + +DROP TABLE t1; + # End of 4.1 tests # diff --git a/mysql-test/t/delayed.test b/mysql-test/t/delayed.test index 5ae757b1fde..55e8f81f763 100644 --- a/mysql-test/t/delayed.test +++ b/mysql-test/t/delayed.test @@ -50,3 +50,52 @@ insert into t1 values (1); insert delayed into t1 values (1); select * from t1; drop table t1; + +# +# Bug #20195: INSERT DELAYED with auto_increment is assigned wrong values +# +CREATE TABLE t1 ( a int(10) NOT NULL auto_increment, PRIMARY KEY (a)); + +# Make one delayed insert to start the separate thread +insert delayed into t1 values(null); + +# Do some normal inserts +insert into t1 values(null); +insert into t1 values(null); + +# Discarded, since the delayed-counter is 2, which is already used +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 3, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 4, which is unused +insert delayed into t1 values(null); + +# Do some more inserts +insert into t1 values(null); +insert into t1 values(null); +insert into t1 values(null); + +# Delete one of the above to make a hole +delete from t1 where a=6; + +# Discarded, since the delayed-counter is 5, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 6, which is unused (the row we deleted) +insert delayed into t1 values(null); + +# Discarded, since the delayed-counter is 7, which is already used +insert delayed into t1 values(null); + +# Works, since the delayed-counter is 8, which is unused +insert delayed into t1 values(null); + +# Check what we have now +# must wait so that the delayed thread finishes +# Note: this must be increased if the test fails +--sleep 1 +select * from t1 order by a; + +DROP TABLE t1; diff --git a/mysql-test/t/fulltext_left_join.test b/mysql-test/t/fulltext_left_join.test index 3bb1f0b7309..7c22f49ed8c 100644 --- a/mysql-test/t/fulltext_left_join.test +++ b/mysql-test/t/fulltext_left_join.test @@ -32,7 +32,7 @@ select match(t1.texte,t1.sujet,t1.motsclefs) against('droit' IN BOOLEAN MODE) drop table t1, t2; # -# Bug #484, reported by Stephen Brandon +# BUG#484, reported by Stephen Brandon # create table t1 (venue_id int(11) default null, venue_text varchar(255) default null, dt datetime default null) engine=myisam; @@ -45,4 +45,17 @@ select * from t1 left join t2 on (venue_id = entity_id and match(name) against(' select * from t1 left join t2 on (venue_id = entity_id and match(name) against('aberdeen')) where dt = '2003-05-23 19:30:00'; drop table t1,t2; +# +# BUG#14708 +# Inconsistent treatment of NULLs in LEFT JOINed FULLTEXT matching without index +# + +create table t1 (id int not null primary key, d char(200) not null, e char(200)); +insert into t1 values (1, 'aword', null), (2, 'aword', 'bword'), (3, 'bword', null), (4, 'bword', 'aword'), (5, 'aword and bword', null); +select * from t1 where match(d, e) against ('+aword +bword' in boolean mode); +create table t2 (m_id int not null, f char(200), key (m_id)); +insert into t2 values (1, 'bword'), (3, 'aword'), (5, ''); +select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode); +drop table t1,t2; + # End of 4.1 tests diff --git a/mysql-test/t/func_concat.test b/mysql-test/t/func_concat.test index 37fc0e105b8..5487ad9c56b 100644 --- a/mysql-test/t/func_concat.test +++ b/mysql-test/t/func_concat.test @@ -52,4 +52,19 @@ select 'a' union select concat('a', -0.0); --replace_result a-0.0000 a0.0000 select 'a' union select concat('a', -0.0000); +# +# Bug#16716: subselect in concat() may lead to a wrong result +# +select concat((select x from (select 'a' as x) as t1 ), + (select y from (select 'b' as y) as t2 )) from (select 1 union select 2 ) + as t3; + # End of 4.1 tests + +# +# Bug#15962: CONCAT() in UNION may lead to a data trucation. +# +create table t1(f1 varchar(6)) charset=utf8; +insert into t1 values ("123456"); +select concat(f1, 2) a from t1 union select 'x' a from t1; +drop table t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index fb9470c16dd..e8c5fa18a25 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -539,6 +539,34 @@ INSERT INTO t1 VALUES SELECT MAX(id) FROM t1 WHERE id < 3 AND a=2 AND b=6; DROP TABLE t1; +# +# Bug #18206: min/max optimization cannot be applied to partial index +# + +CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b)); +INSERT INTO t1 VALUES (1,'xx'), (2,'aa'); +SELECT * FROM t1; + +SELECT MAX(b) FROM t1 WHERE b < 'ppppp'; +SHOW WARNINGS; +SELECT MAX(b) FROM t1 WHERE b < 'pp'; +DROP TABLE t1; + +CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4))); +INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa'); +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (id int , b varchar(512), INDEX(b(250))) COLLATE latin1_bin; +INSERT INTO t1 VALUES + (1,CONCAT(REPEAT('_', 250), "qq")), (1,CONCAT(REPEAT('_', 250), "zz")), + (1,CONCAT(REPEAT('_', 250), "aa")), (1,CONCAT(REPEAT('_', 250), "ff")); + +SELECT MAX(b) FROM t1; +EXPLAIN SELECT MAX(b) FROM t1; +DROP TABLE t1; + # End of 4.1 tests # diff --git a/mysql-test/t/func_in.test b/mysql-test/t/func_in.test index 8ddf1fbe314..5a5e3ec798d 100644 --- a/mysql-test/t/func_in.test +++ b/mysql-test/t/func_in.test @@ -109,6 +109,18 @@ select count(*) from t1 where id not in (1); select count(*) from t1 where id not in (1,2); drop table t1; +# +# Bug#18360 Incorrect type coercion in IN() results in false comparison +# +create table t1 (f1 char(1), f2 int); +insert into t1 values (1,0),('a',1),('z',2); +select f1 from t1 where f1 in (1,'z'); +select f2 from t1 where f2 in (1,'z'); +select f1 from t1 where 'z' in (1,f1); +select * from t1 where 'z' in (f2,f1); +select * from t1 where 1 in (f2,f1); +drop table t1; + # End of 4.1 tests # diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 7f809dbc4a1..b13fe039261 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -673,6 +673,18 @@ drop table t1; select load_file("lkjlkj"); select ifnull(load_file("lkjlkj"),"it's null"); +# +# Bug#15351: Wrong collation used for comparison of md5() and sha() +# parameter can lead to a wrong result. +# +create table t1 (f1 varchar(4), f2 varchar(64), unique key k1 (f1,f2)); +insert into t1 values ( 'test',md5('test')), ('test', sha('test')); +select * from t1 where f1='test' and (f2= md5("test") or f2= md5("TEST")); +select * from t1 where f1='test' and (f2= md5("TEST") or f2= md5("test")); +select * from t1 where f1='test' and (f2= sha("test") or f2= sha("TEST")); +select * from t1 where f1='test' and (f2= sha("TEST") or f2= sha("test")); +drop table t1; + --echo End of 4.1 tests # diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 6366d100bbc..976f7191a5f 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -367,6 +367,27 @@ select last_day('2005-01-00'); select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); +# +# Bug#16377 result of DATE/TIME functions were compared as strings which +# can lead to a wrong result. +# +create table t1(f1 date, f2 time, f3 datetime); +insert into t1 values ("2006-01-01", "12:01:01", "2006-01-01 12:01:01"); +insert into t1 values ("2006-01-02", "12:01:02", "2006-01-02 12:01:02"); +select f1 from t1 where f1 between "2006-1-1" and 20060101; +select f1 from t1 where f1 between "2006-1-1" and "2006.1.1"; +select f1 from t1 where date(f1) between "2006-1-1" and "2006.1.1"; +select f2 from t1 where f2 between "12:1:2" and "12:2:2"; +select f2 from t1 where time(f2) between "12:1:2" and "12:2:2"; +select f3 from t1 where f3 between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f3 from t1 where timestamp(f3) between "2006-1-1 12:1:1" and "2006-1-1 12:1:2"; +select f1 from t1 where "2006-1-1" between f1 and f3; +select f1 from t1 where "2006-1-1" between date(f1) and date(f3); +select f1 from t1 where "2006-1-1" between f1 and 'zzz'; +select f1 from t1 where makedate(2006,1) between date(f1) and date(f3); +select f1 from t1 where makedate(2006,2) between date(f1) and date(f3); +drop table t1; + # # Bug #16546 # diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test index ce19fce678a..e156ccd82a8 100644 --- a/mysql-test/t/join.test +++ b/mysql-test/t/join.test @@ -564,4 +564,29 @@ select a2 from ((t1 natural join t2) join t3 on b=c1) natural join t4; drop table t1,t2,t3,t4; +# +# BUG#15355: Common natural join column not resolved in prepared statement nested query +# +create table t1 (c int, b int); +create table t2 (a int, b int); +create table t3 (b int, c int); +create table t4 (y int, c int); +create table t5 (y int, z int); + +insert into t1 values (3,2); +insert into t2 values (1,2); +insert into t3 values (2,3); +insert into t4 values (1,3); +insert into t5 values (1,4); + +-- this fails +prepare stmt1 from "select * from ((t3 natural join (t1 natural join t2)) +natural join t4) natural join t5"; +execute stmt1; + +-- this works +select * from ((t3 natural join (t1 natural join t2)) natural join t4) + natural join t5; +drop table t1, t2, t3, t4, t5; + # End of tests for WL#2486 - natural/using join diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 04c33e9d709..21271517564 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -452,6 +452,14 @@ insert into t2 values(1,null); delete t2, t1 from t2 left join t1 on (t2.aclid=t1.aclid) where t2.refid='1'; drop table t1, t2; +# +# Bug#19225: unchecked error leads to server crash +# +create table t1(a int); +create table t2(a int); +--error 1093 +delete from t1,t2 using t1,t2 where t1.a=(select a from t1); +drop table t1, t2; # End of 4.1 tests # diff --git a/mysql-test/t/ndb_condition_pushdown.test b/mysql-test/t/ndb_condition_pushdown.test index 3b53f7b3431..9795c831161 100644 --- a/mysql-test/t/ndb_condition_pushdown.test +++ b/mysql-test/t/ndb_condition_pushdown.test @@ -1686,5 +1686,27 @@ select * from t1 where b like 'ab' or b like 'ab'; select * from t1 where b like 'abc'; select * from t1 where b like 'abc' or b like 'abc'; +# bug#20406 (maybe same as bug#17421 -1, not seen on 32-bit x86) +drop table t1; +create table t1 ( fname varchar(255), lname varchar(255) ) +engine=ndbcluster; +insert into t1 values ("Young","Foo"); + +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); + +# make sure optimizer does not do some crazy shortcut +insert into t1 values ("aaa", "aaa"); +insert into t1 values ("bbb", "bbb"); +insert into t1 values ("ccc", "ccc"); +insert into t1 values ("ddd", "ddd"); + +set engine_condition_pushdown = 0; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); +set engine_condition_pushdown = 1; +SELECT fname, lname FROM t1 WHERE (fname like 'Y%') or (lname like 'F%'); + set engine_condition_pushdown = @old_ecpd; DROP TABLE t1,t2,t3,t4,t5; diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test index 0b9a46ad4d3..76da296c0b0 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -320,16 +320,16 @@ prepare stmt4 from ' show status like ''Threads_running'' '; execute stmt4; prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; -prepare stmt4 from ' show engine bdb logs '; # The output depends on the bdb being enabled and on the history # history (actions of the bdb engine). # That is the reason why, we switch the output here off. # (The real output will be tested in ps_6bdb.test) --disable_warnings +prepare stmt4 from ' show engine bdb logs '; +--enable_warnings --disable_result_log execute stmt4; --enable_result_log ---enable_warnings prepare stmt4 from ' show grants for user '; --error 1295 prepare stmt4 from ' show create table t2 '; diff --git a/mysql-test/t/replace.test b/mysql-test/t/replace.test index 10703eaafb8..269854fb180 100644 --- a/mysql-test/t/replace.test +++ b/mysql-test/t/replace.test @@ -35,3 +35,13 @@ select * from t1; drop table t1; # End of 4.1 tests + +# +# Bug#19789: REPLACE was allowed for a VIEW with CHECK OPTION enabled. +# +CREATE TABLE t1 (f1 INT); +CREATE VIEW v1 AS SELECT f1 FROM t1 WHERE f1 = 0 WITH CHECK OPTION; +--error 1369 +REPLACE INTO v1 (f1) VALUES (1); +DROP TABLE t1; +DROP VIEW v1; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 8cd15463c62..ae3981ce47b 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -2217,15 +2217,6 @@ show table status like 't1%'; select 123 as a from t1 where f1 is null; drop table t1,t11; -# Bug 7672 Unknown column error in order clause -# -CREATE TABLE t1 (a INT, b INT); -(SELECT a, b AS c FROM t1) ORDER BY c+1; -(SELECT a, b AS c FROM t1) ORDER BY b+1; -SELECT a, b AS c FROM t1 ORDER BY c+1; -SELECT a, b AS c FROM t1 ORDER BY b+1; -drop table t1; - # # Bug #3874 (function in GROUP and LEFT JOIN) # @@ -2264,6 +2255,21 @@ insert into t2 values(1,1); select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 from t2)); drop table t1,t2; +# +# Bug #4981: 4.x and 5.x produce non-optimal execution path, 3.23 regression test failure +# +CREATE TABLE t1(a int, b int, c int, KEY b(b), KEY c(c)); +insert into t1 values (1,0,0),(2,0,0); +CREATE TABLE t2 (a int, b varchar(2), c varchar(2), PRIMARY KEY(a)); +insert into t2 values (1,'',''), (2,'',''); +CREATE TABLE t3 (a int, b int, PRIMARY KEY (a,b), KEY a (a), KEY b (b)); +insert into t3 values (1,1),(1,2); +# must have "range checked" for t2 +explain select straight_join DISTINCT t2.a,t2.b, t1.c from t1, t3, t2 + where (t1.c=t2.a or (t1.c=t3.a and t2.a=t3.b)) and t1.b=556476786 and + t2.b like '%%' order by t2.b limit 0,1; +DROP TABLE t1,t2,t3; + # # Bug #17873: confusing error message when IGNORE INDEX refers a column name # @@ -2281,48 +2287,6 @@ DROP TABLE t1; # End of 4.1 tests -# -# Test case for bug 7098: substitution of a constant for a string field -# - -CREATE TABLE t1 ( city char(30) ); -INSERT INTO t1 VALUES ('London'); -INSERT INTO t1 VALUES ('Paris'); - -SELECT * FROM t1 WHERE city='London'; -SELECT * FROM t1 WHERE city='london'; -EXPLAIN SELECT * FROM t1 WHERE city='London' AND city='london'; -SELECT * FROM t1 WHERE city='London' AND city='london'; -EXPLAIN SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; -SELECT * FROM t1 WHERE city LIKE '%london%' AND city='London'; - -DROP TABLE t1; - -# -# Bug#7425 inconsistent sort order on unsigned columns result of substraction -# - -create table t1 (a int(11) unsigned, b int(11) unsigned); -insert into t1 values (1,0), (1,1), (1,2); -select a-b from t1 order by 1; -select a-b , (a-b < 0) from t1 order by 1; -select a-b as d, (a-b >= 0), b from t1 group by b having d >= 0; -select cast((a - b) as unsigned) from t1 order by 1; -drop table t1; - - -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - # # Test for bug #6474 # @@ -2357,21 +2321,6 @@ SELECT K2C4, K4N4, F2I4 FROM t1 WHERE K2C4 = 'WART' AND (K2C4 = 'WART' OR K4N4 = '0200'); DROP TABLE t1; -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Bug#8670 # @@ -2410,34 +2359,6 @@ select distinct avg(s1) as x from t1 group by s1 with rollup; drop table t1; -# -# Bug#8733 server accepts malformed query (multiply mentioned distinct) -# -create table t1 (a int(11)); -select all all * from t1; -select distinct distinct * from t1; ---error 1221 -select all distinct * from t1; ---error 1221 -select distinct all * from t1; -drop table t1; - - -# -# Test case for bug 7520: a wrong cost of the index for a BLOB field -# - -CREATE TABLE t1 ( a BLOB, INDEX (a(20)) ); -CREATE TABLE t2 ( a BLOB, INDEX (a(20)) ); - -INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); -INSERT INTO t2 VALUES ('one'),('two'),('three'),('four'),('five'); - -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 USE INDEX (a) ON t1.a=t2.a; -EXPLAIN SELECT * FROM t1 LEFT JOIN t2 FORCE INDEX (a) ON t1.a=t2.a; - -DROP TABLE t1, t2; - # # Test for bug #10084: STRAIGHT_JOIN with ON expression # @@ -2935,3 +2856,29 @@ EXPLAIN SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0; DROP TABLE t1, t2; + +# +# Bug #18895: BIT values cause joins to fail +# +create table t1 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c bit not null +); + +create table t2 ( + a int unsigned not null auto_increment primary key, + b bit not null, + c int unsigned not null, + d varchar(50) +); + +insert into t1 (b,c) values (0,1), (0,1); +insert into t2 (b,c) values (0,1); + +-- Row 1 should succeed. Row 2 should fail. Both fail. +select t1.a, t1.b + 0, t1.c + 0, t2.a, t2.b + 0, t2.c, t2.d +from t1 left outer join t2 on t1.a = t2.c and t2.b <> 1 +where t1.b <> 1 order by t1.a; + +drop table t1,t2; diff --git a/mysql-test/t/type_newdecimal.test b/mysql-test/t/type_newdecimal.test index 74782a5bddb..35aff8b3c5a 100644 --- a/mysql-test/t/type_newdecimal.test +++ b/mysql-test/t/type_newdecimal.test @@ -1095,3 +1095,12 @@ insert into t1 values( insert into t1 values(1e100); select * from t1; drop table t1; + +# +# Bug#19667 group by a decimal expression yields wrong result +# +create table t1 (i int, j int); +insert into t1 values (1,1), (1,2), (2,3), (2,4); +select i, count(distinct j) from t1 group by i; +select i+0.0 as i2, count(distinct j) from t1 group by i2; +drop table t1; diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 692f1f509fa..7dfe4ac482f 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -793,3 +793,51 @@ select id from t1 union all select 99 order by 1; drop table t1; # End of 4.1 tests + +# +# Bug#18175: Union select over 129 tables with a sum function fails. +# +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)) union +(select avg(1)) union (select avg(1)) union (select avg(1)); + diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index aa420689149..2a1977702c2 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -813,3 +813,42 @@ SELECT * FROM v; DROP VIEW v; DROP TABLE t1; USE test; + +# +# Bug#20363: Create view on just created view is now denied +# +eval CREATE USER mysqltest_db1@localhost identified by 'PWD'; +eval GRANT ALL ON mysqltest_db1.* TO mysqltest_db1@localhost WITH GRANT OPTION; + +# The session with the non root user is needed. +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +connect (session1,localhost,mysqltest_db1,PWD,test); + +CREATE SCHEMA mysqltest_db1 ; +USE mysqltest_db1 ; + +CREATE TABLE t1 (f1 INTEGER); + +CREATE VIEW view1 AS +SELECT * FROM t1; +SHOW CREATE VIEW view1; + +CREATE VIEW view2 AS +SELECT * FROM view1; +--echo # Here comes a suspicious warning +SHOW CREATE VIEW view2; +--echo # But the view view2 is usable +SELECT * FROM view2; + +CREATE VIEW view3 AS +SELECT * FROM view2; + +SELECT * from view3; + +connection default; +DROP VIEW mysqltest_db1.view3; +DROP VIEW mysqltest_db1.view2; +DROP VIEW mysqltest_db1.view1; +DROP TABLE mysqltest_db1.t1; +DROP SCHEMA mysqltest_db1; +DROP USER mysqltest_db1@localhost; diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 9061a71364d..dc0f6bf1977 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -408,6 +408,19 @@ } # +# BUG#19940: NDB sends uninitialized parts of field buffers across the wire. +# This is "works as designed"; the uninitialized part is not used at the +# other end (but Valgrind cannot see this). +# +{ + bug19940 + Memcheck:Param + socketcall.sendto(msg) + fun:send + fun:_ZN15TCP_Transporter6doSendEv + fun:_ZN19TransporterRegistry11performSendEv + fun:_ZN19TransporterRegistry14forceSendCheckEi +} # Warning when printing stack trace (to suppress some not needed warnings) # diff --git a/sql/field.cc b/sql/field.cc index 1f65adca2d5..af81214adda 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -50,8 +50,8 @@ const char field_separator=','; #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) -#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table->read_set || bitmap_is_set(table->read_set, field_index)) -#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table->write_set || bitmap_is_set(table->write_set, field_index)) +#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) +#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index))) /* Rules for merging different types of fields in UNION @@ -3471,8 +3471,7 @@ int Field_long::store(longlong nr, bool unsigned_val) ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; - DBUG_ASSERT(table->in_use == current_thd); // General safety - + if (unsigned_flag) { if (nr < 0 && !unsigned_val) @@ -4605,11 +4604,11 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) int error; bool have_smth_to_conv; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ have_smth_to_conv= (str_to_datetime(from, len, &l_time, - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & MODE_NO_ZERO_DATE) | MODE_NO_ZERO_IN_DATE, &error) > MYSQL_TIMESTAMP_ERROR); @@ -4675,7 +4674,7 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) my_time_t timestamp= 0; int error; my_bool in_dst_time_gap; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */ longlong tmp= number_to_datetime(nr, &l_time, (thd->variables.sql_mode & @@ -4731,7 +4730,7 @@ longlong Field_timestamp::val_int(void) ASSERT_COLUMN_MARKED_FOR_READ; uint32 temp; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4757,7 +4756,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) ASSERT_COLUMN_MARKED_FOR_READ; uint32 temp, temp2; TIME time_tmp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; char *to; val_buffer->alloc(field_length+1); @@ -4828,7 +4827,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate) { long temp; - THD *thd= table->in_use; + THD *thd= table ? table->in_use : current_thd; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) temp=uint4korr(ptr); @@ -4911,7 +4910,8 @@ void Field_timestamp::sql_type(String &res) const void Field_timestamp::set_time() { - long tmp= (long) table->in_use->query_start(); + THD *thd= table ? table->in_use : current_thd; + long tmp= (long) thd->query_start(); set_notnull(); #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4978,7 +4978,7 @@ int Field_time::store_time(TIME *ltime, timestamp_type type) (ltime->minute * 100 + ltime->second); if (ltime->neg) tmp= -tmp; - return Field_time::store((longlong) tmp); + return Field_time::store((longlong) tmp, TRUE); } @@ -4990,7 +4990,7 @@ int Field_time::store(double nr) if (nr > 8385959.0) { tmp=8385959L; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME); error= 1; } @@ -5108,12 +5108,13 @@ String *Field_time::val_str(String *val_buffer, bool Field_time::get_date(TIME *ltime, uint fuzzydate) { long tmp; + THD *thd= table ? table->in_use : current_thd; if (!(fuzzydate & TIME_FUZZY_DATE)) { - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, - table->in_use->row_count); + thd->row_count); return 1; } tmp=(long) sint3korr(ptr); @@ -5306,9 +5307,10 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) TIME l_time; uint32 tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5362,9 +5364,10 @@ int Field_date::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5514,9 +5517,10 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) TIME l_time; long tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (str_to_datetime(from, len, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) <= MYSQL_TIMESTAMP_ERROR) @@ -5555,9 +5559,10 @@ int Field_newdate::store(longlong nr, bool unsigned_val) TIME l_time; longlong tmp; int error; + THD *thd= table ? table->in_use : current_thd; if (number_to_datetime(nr, &l_time, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error) == LL(-1)) @@ -5705,10 +5710,11 @@ int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) int error; ulonglong tmp= 0; enum enum_mysql_timestamp_type func_res; + THD *thd= table ? table->in_use : current_thd; func_res= str_to_datetime(from, len, &time_tmp, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -5756,9 +5762,10 @@ int Field_datetime::store(longlong nr, bool unsigned_val) TIME not_used; int error; longlong initial_nr= nr; + THD *thd= table ? table->in_use : current_thd; nr= number_to_datetime(nr, ¬_used, (TIME_FUZZY_DATE | - (table->in_use->variables.sql_mode & + (thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES))), &error); @@ -6174,17 +6181,6 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) { uint a_len, b_len; - if (field_charset->strxfrm_multiply > 1) - { - /* - We have to remove end space to be able to compare multi-byte-characters - like in latin_de 'ae' and 0xe4 - */ - return field_charset->coll->strnncollsp(field_charset, - (const uchar*) a_ptr, field_length, - (const uchar*) b_ptr, - field_length, 0); - } if (field_charset->mbmaxlen != 1) { uint char_len= field_length/field_charset->mbmaxlen; @@ -6193,8 +6189,14 @@ int Field_string::cmp(const char *a_ptr, const char *b_ptr) } else a_len= b_len= field_length; - return my_strnncoll(field_charset,(const uchar*) a_ptr, a_len, - (const uchar*) b_ptr, b_len); + /* + We have to remove end space to be able to compare multi-byte-characters + like in latin_de 'ae' and 0xe4 + */ + return field_charset->coll->strnncollsp(field_charset, + (const uchar*) a_ptr, a_len, + (const uchar*) b_ptr, b_len, + 0); } @@ -7117,7 +7119,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) int Field_blob::store(double nr) { CHARSET_INFO *cs=charset(); - value.set(nr, 2, cs); + value.set_real(nr, 2, cs); return Field_blob::store(value.ptr(),(uint) value.length(), cs); } @@ -7125,10 +7127,7 @@ int Field_blob::store(double nr) int Field_blob::store(longlong nr, bool unsigned_val) { CHARSET_INFO *cs=charset(); - if (unsigned_val) - value.set((ulonglong) nr, cs); - else - value.set(nr, cs); + value.set_int(nr, unsigned_val, cs); return Field_blob::store(value.ptr(), (uint) value.length(), cs); } @@ -8219,7 +8218,7 @@ int Field_bit::store_decimal(const my_decimal *val) { int err= 0; longlong i= convert_decimal2longlong(val, 1, &err); - return test(err | store(i)); + return test(err | store(i, TRUE)); } @@ -9251,7 +9250,11 @@ bool Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, int cuted_increment) { - THD *thd= table->in_use; + /* + If this field was created only for type conversion purposes it + will have table == NULL. + */ + THD *thd= table ? table->in_use : current_thd; if (thd->count_cuted_fields) { thd->cuted_fields+= cuted_increment; @@ -9286,9 +9289,10 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, const char *str, uint str_length, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) - make_truncated_value_warning(table->in_use, str, str_length, ts_type, + make_truncated_value_warning(thd, str, str_length, ts_type, field_name); } @@ -9315,13 +9319,13 @@ Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, longlong nr, timestamp_type ts_type, int cuted_increment) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, cuted_increment)) { char str_nr[22]; char *str_end= longlong10_to_str(nr, str_nr, -10); - make_truncated_value_warning(table->in_use, str_nr, - (uint) (str_end - str_nr), + make_truncated_value_warning(thd, str_nr, (uint) (str_end - str_nr), ts_type, field_name); } } @@ -9347,13 +9351,15 @@ void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, uint code, double nr, timestamp_type ts_type) { - if (table->in_use->really_abort_on_warning() || + THD *thd= table ? table->in_use : current_thd; + if (thd->really_abort_on_warning() || set_warning(level, code, 1)) { /* DBL_DIG is enough to print '-[digits].E+###' */ char str_nr[DBL_DIG + 8]; uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); - make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type, + make_truncated_value_warning(thd, str_nr, str_len, ts_type, field_name); } } + diff --git a/sql/field.h b/sql/field.h index 2ac7ec2c69d..69cb641f158 100644 --- a/sql/field.h +++ b/sql/field.h @@ -124,7 +124,7 @@ public: static bool type_can_have_key_part(enum_field_types); static enum_field_types field_type_merge(enum_field_types, enum_field_types); static Item_result result_merge_type(enum_field_types); - bool eq(Field *field) + virtual bool eq(Field *field) { return (ptr == field->ptr && null_ptr == field->null_ptr && null_bit == field->null_bit); @@ -1387,6 +1387,13 @@ public: bit_ptr= bit_ptr_arg; bit_ofs= bit_ofs_arg; } + bool eq(Field *field) + { + return (Field::eq(field) && + field->type() == type() && + bit_ptr == ((Field_bit *)field)->bit_ptr && + bit_ofs == ((Field_bit *)field)->bit_ofs); + } void move_field_offset(my_ptrdiff_t ptr_diff) { Field::move_field_offset(ptr_diff); diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 2d623702670..6c66ba620c9 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -10364,7 +10364,7 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->set_null(); // DELETED_ROWS table->field[c++]->set_null(); // UPDATE_COUNT table->field[c++]->store(lfg.getUndoFreeWords()); // FREE_EXTENTS - table->field[c++]->store(lfg.getUndoBufferSize()); // TOTAL_EXTENTS + table->field[c++]->store(uf.getSize()/4); // TOTAL_EXTENTS table->field[c++]->store(4); // EXTENT_SIZE table->field[c++]->store(uf.getSize()); // INITIAL_SIZE @@ -10394,8 +10394,8 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, table->field[c++]->store("NORMAL", 6, system_charset_info); - char extra[30]; - int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u",id); + char extra[100]; + int len= my_snprintf(extra,sizeof(extra),"CLUSTER_NODE=%u;UNDO_BUFFER_SIZE=%lu",id,lfg.getUndoBufferSize()); table->field[c]->store(extra, len, system_charset_info); schema_table_store_record(thd, table); } diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 022c8a021cb..a88d796dcbd 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -453,7 +453,7 @@ static void ndbcluster_binlog_wait(THD *thd) } /* - Called from MYSQL_LOG::reset_logs in log.cc when binlog is emptied + Called from MYSQL_BIN_LOG::reset_logs in log.cc when binlog is emptied */ static int ndbcluster_reset_logs(THD *thd) { @@ -477,7 +477,7 @@ static int ndbcluster_reset_logs(THD *thd) } /* - Called from MYSQL_LOG::purge_logs in log.cc when the binlog "file" + Called from MYSQL_BIN_LOG::purge_logs in log.cc when the binlog "file" is removed */ diff --git a/sql/item.cc b/sql/item.cc index 5fa3ad61c15..53797052788 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -61,7 +61,7 @@ Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const String * Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const { - to->set(val->real, decimals, &my_charset_bin); + to->set_real(val->real, decimals, &my_charset_bin); return to; } @@ -202,7 +202,7 @@ String *Item::val_string_from_real(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals, &my_charset_bin); + str->set_real(nr,decimals, &my_charset_bin); return str; } @@ -212,10 +212,7 @@ String *Item::val_string_from_int(String *str) longlong nr= val_int(); if (null_value) return 0; - if (unsigned_flag) - str->set((ulonglong) nr, &my_charset_bin); - else - str->set(nr, &my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -2041,7 +2038,7 @@ String *Item_float::val_str(String *str) { // following assert is redundant, because fixed=1 assigned in constructor DBUG_ASSERT(fixed == 1); - str->set(value,decimals,&my_charset_bin); + str->set_real(value,decimals,&my_charset_bin); return str; } @@ -2636,7 +2633,7 @@ String *Item_param::val_str(String* str) case LONG_DATA_VALUE: return &str_value_ptr; case REAL_VALUE: - str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); return str; case INT_VALUE: str->set(value.integer, &my_charset_bin); @@ -2676,7 +2673,7 @@ const String *Item_param::query_val_str(String* str) const str->set(value.integer, &my_charset_bin); break; case REAL_VALUE: - str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); + str->set_real(value.real, NOT_FIXED_DEC, &my_charset_bin); break; case DECIMAL_VALUE: if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, @@ -4329,7 +4326,7 @@ void Item_float::print(String *str) } char buffer[20]; String num(buffer, sizeof(buffer), &my_charset_bin); - num.set(value, decimals, &my_charset_bin); + num.set_real(value, decimals, &my_charset_bin); str->append(num); } @@ -5771,7 +5768,7 @@ longlong Item_cache_real::val_int() String* Item_cache_real::val_str(String *str) { DBUG_ASSERT(fixed == 1); - str->set(value, decimals, default_charset()); + str->set_real(value, decimals, default_charset()); return str; } diff --git a/sql/item.h b/sql/item.h index 54cab34eb78..a6132aba8b0 100644 --- a/sql/item.h +++ b/sql/item.h @@ -857,6 +857,14 @@ public: { return 0; } + /* + result_as_longlong() must return TRUE for Items representing DATE/TIME + functions and DATE/TIME table fields. + Those Items have result_type()==STRING_RESULT (and not INT_RESULT), but + their values should be compared as integers (because the integer + representation is more precise than the string one). + */ + virtual bool result_as_longlong() { return FALSE; } }; @@ -1283,6 +1291,10 @@ public: bool register_field_in_read_map(byte *arg); bool check_partition_func_processor(byte *bool_arg) { return 0; } void cleanup(); + bool result_as_longlong() + { + return field->can_be_compared_as_longlong(); + } Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item *equal_fields_propagator(byte *arg); Item *set_no_const_sub(byte *arg); @@ -1904,6 +1916,10 @@ public: bool walk(Item_processor processor, bool walk_subquery, byte *arg) { return (*ref)->walk(processor, walk_subquery, arg); } void print(String *str); + bool result_as_longlong() + { + return (*ref)->result_as_longlong(); + } void cleanup(); Item_field *filed_for_view_update() { return (*ref)->filed_for_view_update(); } diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 9db2f465080..1661f04a4ae 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -132,7 +132,7 @@ bool Cached_item_decimal::cmp() { my_decimal tmp; my_decimal *ptmp= item->val_decimal(&tmp); - if (null_value != item->null_value || my_decimal_cmp(&value, ptmp) == 0) + if (null_value != item->null_value || my_decimal_cmp(&value, ptmp)) { null_value= item->null_value; my_decimal2decimal(ptmp, &value); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3a1f4b50458..3bff7878f02 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -63,25 +63,144 @@ static void agg_result_type(Item_result *type, Item **items, uint nitems) } +/* + Aggregates result types from the array of items. + + SYNOPSIS: + agg_cmp_type() + thd thread handle + type [out] the aggregated type + items array of items to aggregate the type from + nitems number of items in the array + + DESCRIPTION + This function aggregates result types from the array of items. Found type + supposed to be used later for comparison of values of these items. + Aggregation itself is performed by the item_cmp_type() function. + + NOTES + Aggregation rules: + If all items are constants the type will be aggregated from all items. + If there are some non-constant items then only types of non-constant + items will be used for aggregation. + If there are DATE/TIME fields/functions in the list and no string + fields/functions in the list then: + The INT_RESULT type will be used for aggregation instead of original + result type of any DATE/TIME field/function in the list + All constant items in the list will be converted to a DATE/TIME using + found field or result field of found function. + + Implementation notes: + The code is equivalent to: + 1. Check the list for presence of a STRING field/function. + Collect the is_const flag. + 2. Get a Field* object to use for type coercion + 3. Perform type conversion. + 1 and 2 are implemented in 2 loops. The first searches for a DATE/TIME + field/function and checks presence of a STRING field/function. + The second loop works only if a DATE/TIME field/function is found. + It checks presence of a STRING field/function in the rest of the list. + + TODO + 1) The current implementation can produce false comparison results for + expressions like: + date_time_field BETWEEN string_field_with_dates AND string_constant + if the string_constant will omit some of leading zeroes. + In order to fully implement correct comparison of DATE/TIME the new + DATETIME_RESULT result type should be introduced and agg_cmp_type() + should return the DATE/TIME field used for the conversion. Later + this field can be used by comparison functions like Item_func_between to + convert string values to ints on the fly and thus return correct results. + This modification will affect functions BETWEEN, IN and CASE. + + 2) If in the list a DATE field/function and a DATETIME field/function + are present in the list then the first found field/function will be + used for conversion. This may lead to wrong results and probably should + be fixed. +*/ + static void agg_cmp_type(THD *thd, Item_result *type, Item **items, uint nitems) { uint i; + Item::Type res= (Item::Type)0; + /* Used only for date/time fields, max_length = 19 */ + char buff[20]; + uchar null_byte; Field *field= NULL; - /* If the first argument is a FIELD_ITEM, pull out the field. */ - if (items[0]->real_item()->type() == Item::FIELD_ITEM) - field=((Item_field *)(items[0]->real_item()))->field; - /* But if it can't be compared as a longlong, we don't really care. */ - if (field && !field->can_be_compared_as_longlong()) - field= NULL; - - type[0]= items[0]->result_type(); - for (i= 1; i < nitems; i++) + /* Search for date/time fields/functions */ + for (i= 0; i < nitems; i++) { - type[0]= item_cmp_type(type[0], items[i]->result_type()); - if (field && convert_constant_item(thd, field, &items[i])) - type[0]= INT_RESULT; + if (!items[i]->result_as_longlong()) + { + /* Do not convert anything if a string field/function is present */ + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT) + { + i= nitems; + break; + } + continue; + } + if ((res= items[i]->real_item()->type()) == Item::FIELD_ITEM) + { + field= ((Item_field *)items[i]->real_item())->field; + break; + } + else if (res == Item::FUNC_ITEM) + { + field= items[i]->tmp_table_field_from_field_type(0, 0); + if (field) + field->move_field(buff, &null_byte, 0); + break; + } } + if (field) + { + /* Check the rest of the list for presence of a string field/function. */ + for (i++ ; i < nitems; i++) + { + if (!items[i]->const_item() && items[i]->result_type() == STRING_RESULT && + !items[i]->result_as_longlong()) + { + if (res == Item::FUNC_ITEM) + delete field; + field= 0; + break; + } + } + } + /* Reset to 0 on first occurence of non-const item. 1 otherwise */ + bool is_const= items[0]->const_item(); + /* + If the first item is a date/time function then its result should be + compared as int + */ + if (field) + { + /* Suppose we are comparing dates and some non-constant items are present. */ + type[0]= INT_RESULT; + is_const= 0; + } + else + type[0]= items[0]->result_type(); + + for (i= 0; i < nitems ; i++) + { + if (!items[i]->const_item()) + { + Item_result result= field && items[i]->result_as_longlong() ? + INT_RESULT : items[i]->result_type(); + type[0]= is_const ? result : item_cmp_type(type[0], result); + is_const= 0; + } + else if (is_const) + type[0]= item_cmp_type(type[0], items[i]->result_type()); + else if (field) + convert_constant_item(thd, field, &items[i]); + } + + if (res == Item::FUNC_ITEM && field) + delete field; } @@ -235,17 +354,21 @@ longlong Item_func_nop_all::val_int() static bool convert_constant_item(THD *thd, Field *field, Item **item) { int result= 0; + if (!(*item)->with_subselect && (*item)->const_item()) { - /* For comparison purposes allow invalid dates like 2000-01-32 */ TABLE *table= field->table; - ulong orig_sql_mode= table->in_use->variables.sql_mode; - my_bitmap_map *old_write_map= - dbug_tmp_use_all_columns(table, table->write_set); - my_bitmap_map *old_read_map= - dbug_tmp_use_all_columns(table, table->read_set); + ulong orig_sql_mode= thd->variables.sql_mode; + my_bitmap_map *old_write_map; + my_bitmap_map *old_read_map; - table->in_use->variables.sql_mode|= MODE_INVALID_DATES; + if (table) + { + old_write_map= dbug_tmp_use_all_columns(table, table->write_set); + old_read_map= dbug_tmp_use_all_columns(table, table->read_set); + } + /* For comparison purposes allow invalid dates like 2000-01-32 */ + thd->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { Item *tmp= new Item_int_with_ref(field->val_int(), *item, @@ -254,9 +377,12 @@ static bool convert_constant_item(THD *thd, Field *field, Item **item) thd->change_item_tree(item, tmp); result= 1; // Item was replaced } - table->in_use->variables.sql_mode= orig_sql_mode; - dbug_tmp_restore_column_map(table->write_set, old_write_map); - dbug_tmp_restore_column_map(table->read_set, old_read_map); + thd->variables.sql_mode= orig_sql_mode; + if (table) + { + dbug_tmp_restore_column_map(table->write_set, old_write_map); + dbug_tmp_restore_column_map(table->read_set, old_read_map); + } } return result; } @@ -1106,9 +1232,8 @@ void Item_func_between::fix_length_and_dec() return; agg_cmp_type(thd, &cmp_type, args, 3); - if (cmp_type == STRING_RESULT && - agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV)) - return; + if (cmp_type == STRING_RESULT) + agg_arg_charsets(cmp_collation, args, 3, MY_COLL_CMP_CONV); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 8bd1b53e226..82cb5febe7d 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -46,7 +46,7 @@ public: inline int set_compare_func(Item_bool_func2 *owner_arg) { return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(), - (*b)->result_type())); + (*b)->result_type())); } inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2, @@ -59,8 +59,9 @@ public: inline int set_cmp_func(Item_bool_func2 *owner_arg, Item **a1, Item **a2) { - return set_cmp_func(owner_arg, a1, a2, item_cmp_type((*a1)->result_type(), - (*a2)->result_type())); + return set_cmp_func(owner_arg, a1, a2, + item_cmp_type((*a1)->result_type(), + (*a2)->result_type())); } inline int compare() { return (this->*func)(); } diff --git a/sql/item_func.cc b/sql/item_func.cc index 3b633295f20..8139ba81777 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -416,7 +416,7 @@ String *Item_real_func::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals, &my_charset_bin); + str->set_real(nr,decimals, &my_charset_bin); return str; } @@ -556,10 +556,7 @@ String *Item_int_func::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -701,10 +698,7 @@ String *Item_func_numhybrid::val_str(String *str) longlong nr= int_op(); if (null_value) return 0; /* purecov: inspected */ - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); break; } case REAL_RESULT: @@ -712,7 +706,7 @@ String *Item_func_numhybrid::val_str(String *str) double nr= real_op(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); break; } case STRING_RESULT: @@ -2058,10 +2052,7 @@ String *Item_func_min_max::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } case DECIMAL_RESULT: @@ -2077,7 +2068,7 @@ String *Item_func_min_max::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); return str; } case STRING_RESULT: @@ -2828,7 +2819,7 @@ String *Item_func_udf_float::val_str(String *str) double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + str->set_real(nr,decimals,&my_charset_bin); return str; } @@ -2847,10 +2838,7 @@ String *Item_func_udf_int::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + str->set_int(nr, unsigned_flag, &my_charset_bin); return str; } @@ -3300,7 +3288,7 @@ longlong Item_func_last_insert_id::val_int() return value; // Avoid side effect of insert_id() } thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return thd->insert_id(); + return thd->last_insert_id_used ? thd->current_insert_id : thd->insert_id(); } /* This function is just used to test speed of different functions */ @@ -3650,7 +3638,7 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, switch (type) { case REAL_RESULT: - str->set(*(double*) value, decimals, &my_charset_bin); + str->set_real(*(double*) value, decimals, &my_charset_bin); break; case INT_RESULT: str->set(*(longlong*) value, &my_charset_bin); @@ -4516,7 +4504,7 @@ double Item_func_match::val_real() if (ft_handler == NULL) DBUG_RETURN(-1.0); - if (table->null_row) /* NULL row from an outer join */ + if (key != NO_SUCH_KEY && table->null_row) /* NULL row from an outer join */ DBUG_RETURN(0.0); if (join_key) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index eb1fbf4855d..bffaa163a05 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -154,7 +154,15 @@ String *Item_func_md5::val_str(String *str) void Item_func_md5::fix_length_and_dec() { - max_length=32; + max_length=32; + /* + The MD5() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } @@ -195,7 +203,15 @@ String *Item_func_sha::val_str(String *str) void Item_func_sha::fix_length_and_dec() { - max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash + /* + The SHA() function treats its parameter as being a case sensitive. Thus + we set binary collation on it so different instances of MD5() will be + compared properly. + */ + args[0]->collation.set( + get_charset_by_csname(args[0]->collation.collation->csname, + MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); } @@ -288,11 +304,14 @@ String *Item_func_concat::val_str(String *str) DBUG_ASSERT(fixed == 1); String *res,*res2,*use_as_buff; uint i; + bool is_const= 0; null_value=0; if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; + /* Item_subselect in --ps-protocol mode will state it as a non-const */ + is_const= args[0]->const_item() || !args[0]->used_tables(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) @@ -315,7 +334,7 @@ String *Item_func_concat::val_str(String *str) current_thd->variables.max_allowed_packet); goto null; } - if (res->alloced_length() >= res->length()+res2->length()) + if (!is_const && res->alloced_length() >= res->length()+res2->length()) { // Use old buffer res->append(*res2); } @@ -370,6 +389,7 @@ String *Item_func_concat::val_str(String *str) res= &tmp_value; use_as_buff=str; } + is_const= 0; } } res->set_charset(collation.collation); @@ -389,7 +409,14 @@ void Item_func_concat::fix_length_and_dec() return; for (uint i=0 ; i < arg_count ; i++) - max_result_length+= args[i]->max_length; + { + if (args[i]->collation.collation->mbmaxlen != collation.collation->mbmaxlen) + max_result_length+= (args[i]->max_length / + args[i]->collation.collation->mbmaxlen) * + collation.collation->mbmaxlen; + else + max_result_length+= args[i]->max_length; + } if (max_result_length >= MAX_BLOB_WIDTH) { @@ -1808,7 +1835,7 @@ String *Item_func_format::val_str(String *str) return 0; /* purecov: inspected */ nr= my_double_round(nr, decimals, FALSE); /* Here default_charset() is right as this is not an automatic conversion */ - str->set(nr,decimals, default_charset()); + str->set_real(nr,decimals, default_charset()); if (isnan(nr)) return str; str_length=str->length(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 1285e842769..d6bc2c326d6 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1578,16 +1578,13 @@ Item_sum_hybrid::val_str(String *str) case STRING_RESULT: return &value; case REAL_RESULT: - str->set(sum,decimals, &my_charset_bin); + str->set_real(sum,decimals, &my_charset_bin); break; case DECIMAL_RESULT: my_decimal2string(E_DEC_FATAL_ERROR, &sum_dec, 0, 0, 0, str); return str; case INT_RESULT: - if (unsigned_flag) - str->set((ulonglong) sum_int, &my_charset_bin); - else - str->set((longlong) sum_int, &my_charset_bin); + str->set_int(sum_int, unsigned_flag, &my_charset_bin); break; case ROW_RESULT: default: diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 06abbe60121..d38069e54da 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1503,7 +1503,7 @@ double Item_func_sysdate_local::val_real() { DBUG_ASSERT(fixed == 1); store_now_in_TIME(<ime); - return (longlong) TIME_to_ulonglong_datetime(<ime); + return ulonglong2double(TIME_to_ulonglong_datetime(<ime)); } @@ -2344,6 +2344,20 @@ String *Item_datetime_typecast::val_str(String *str) } +longlong Item_datetime_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (get_arg0_date(<ime,1)) + { + null_value= 1; + return 0; + } + + return TIME_to_ulonglong_datetime(<ime); +} + + bool Item_time_typecast::get_time(TIME *ltime) { bool res= get_arg0_time(ltime); @@ -2358,6 +2372,17 @@ bool Item_time_typecast::get_time(TIME *ltime) } +longlong Item_time_typecast::val_int() +{ + TIME ltime; + if (get_time(<ime)) + { + null_value= 1; + return 0; + } + return ltime.hour * 10000L + ltime.minute * 100 + ltime.second; +} + String *Item_time_typecast::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2397,6 +2422,14 @@ String *Item_date_typecast::val_str(String *str) return 0; } +longlong Item_date_typecast::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME ltime; + if (args[0]->get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return (longlong) (ltime.year * 10000L + ltime.month * 100 + ltime.day); +} /* MAKEDATE(a,b) is a date function that creates a date value @@ -2433,6 +2466,33 @@ err: } +longlong Item_func_makedate::val_int() +{ + DBUG_ASSERT(fixed == 1); + TIME l_time; + long daynr= (long) args[1]->val_int(); + long yearnr= (long) args[0]->val_int(); + long days; + + if (args[0]->null_value || args[1]->null_value || + yearnr < 0 || daynr <= 0) + goto err; + + days= calc_daynr(yearnr,1,1) + daynr - 1; + /* Day number from year 0 to 9999-12-31 */ + if (days >= 0 && days < MAX_DAY_NUMBER) + { + null_value=0; + get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); + return (longlong) (l_time.year * 10000L + l_time.month * 100 + l_time.day); + } + +err: + null_value= 1; + return 0; +} + + void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 513ef692ed7..e3f10c9716f 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -365,6 +365,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } }; @@ -380,6 +381,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } }; @@ -409,6 +411,7 @@ public: TIME representation using UTC-SYSTEM or per-thread time zone. */ virtual void store_now_in_TIME(TIME *now_time)=0; + bool result_as_longlong() { return TRUE; } }; @@ -647,6 +650,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -768,6 +772,8 @@ public: max_length= 10; maybe_null= 1; } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -784,6 +790,8 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; @@ -799,6 +807,8 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); }; class Item_func_makedate :public Item_str_func @@ -817,6 +827,8 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool result_as_longlong() { return TRUE; } + longlong val_int(); bool check_partition_func_processor(byte *bool_arg) { return 0;} }; diff --git a/sql/log.cc b/sql/log.cc index c6c63ceb929..b6b1eb721bb 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -39,7 +39,7 @@ LOGGER logger; -MYSQL_LOG mysql_bin_log; +MYSQL_BIN_LOG mysql_bin_log; ulong sync_binlog_counter= 0; static bool test_if_number(const char *str, @@ -90,8 +90,8 @@ handlerton binlog_hton; SYNOPSIS open_log_table() - log_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW + log_table_type type of the log table to open: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW DESCRIPTION @@ -106,14 +106,14 @@ handlerton binlog_hton; TRUE - error occured */ -bool Log_to_csv_event_handler::open_log_table(uint log_type) +bool Log_to_csv_event_handler::open_log_table(uint log_table_type) { THD *log_thd, *curr= current_thd; TABLE_LIST *table; bool error= FALSE; DBUG_ENTER("open_log_table"); - switch (log_type) { + switch (log_table_type) { case QUERY_LOG_GENERAL: log_thd= general_log_thd; table= &general_log; @@ -226,8 +226,8 @@ Log_to_csv_event_handler::~Log_to_csv_event_handler() SYNOPSIS reopen_log_table() - log_type type of the log table to open: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW + log_table_type type of the log table to open: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW DESCRIPTION @@ -244,12 +244,12 @@ Log_to_csv_event_handler::~Log_to_csv_event_handler() TRUE - open_log_table() returned an error */ -bool Log_to_csv_event_handler::reopen_log_table(uint log_type) +bool Log_to_csv_event_handler::reopen_log_table(uint log_table_type) { /* don't open the log table, if it wasn't enabled during startup */ if (!logger.is_log_tables_initialized) return FALSE; - return open_log_table(log_type); + return open_log_table(log_table_type); } void Log_to_csv_event_handler::cleanup() @@ -319,9 +319,9 @@ bool Log_to_csv_event_handler:: table->field[1]->store(user_host, user_host_len, client_cs); table->field[1]->set_notnull(); - table->field[2]->store((longlong) thread_id); + table->field[2]->store((longlong) thread_id, TRUE); table->field[2]->set_notnull(); - table->field[3]->store((longlong) server_id); + table->field[3]->store((longlong) server_id, TRUE); table->field[3]->set_notnull(); table->field[4]->store(command_type, command_type_len, client_cs); table->field[4]->set_notnull(); @@ -401,13 +401,13 @@ bool Log_to_csv_event_handler:: if (query_start_arg) { /* fill in query_time field */ - table->field[2]->store(query_time); + table->field[2]->store(query_time, TRUE); /* lock_time */ - table->field[3]->store(lock_time); + table->field[3]->store(lock_time, TRUE); /* rows_sent */ - table->field[4]->store((longlong) thd->sent_row_count); + table->field[4]->store((longlong) thd->sent_row_count, TRUE); /* rows_examined */ - table->field[5]->store((longlong) thd->examined_row_count); + table->field[5]->store((longlong) thd->examined_row_count, TRUE); } else { @@ -426,18 +426,18 @@ bool Log_to_csv_event_handler:: if (thd->last_insert_id_used) { - table->field[7]->store((longlong) thd->current_insert_id); + table->field[7]->store((longlong) thd->current_insert_id, TRUE); table->field[7]->set_notnull(); } /* set value if we do an insert on autoincrement column */ if (thd->insert_id_used) { - table->field[8]->store((longlong) thd->last_insert_id); + table->field[8]->store((longlong) thd->last_insert_id, TRUE); table->field[8]->set_notnull(); } - table->field[9]->store((longlong) server_id); + table->field[9]->store((longlong) server_id, TRUE); table->field[9]->set_notnull(); /* sql_text */ @@ -533,9 +533,9 @@ void Log_to_file_event_handler::flush() { /* reopen log files */ if (opt_log) - mysql_log.new_file(1); + mysql_log.reopen_file(); if (opt_slow_log) - mysql_slow_log.new_file(1); + mysql_slow_log.reopen_file(); } /* @@ -590,9 +590,9 @@ void LOGGER::cleanup_end() } -void LOGGER::close_log_table(uint log_type, bool lock_in_use) +void LOGGER::close_log_table(uint log_table_type, bool lock_in_use) { - table_log_handler->close_log_table(log_type, lock_in_use); + table_log_handler->close_log_table(log_table_type, lock_in_use); } @@ -632,9 +632,9 @@ void LOGGER::init_log_tables() } -bool LOGGER::reopen_log_table(uint log_type) +bool LOGGER::reopen_log_table(uint log_table_type) { - return table_log_handler->reopen_log_table(log_type); + return table_log_handler->reopen_log_table(log_table_type); } @@ -1082,9 +1082,9 @@ int LOGGER::set_handlers(uint error_log_printer, SYNOPSIS close_log_table() - log_type type of the log table to close: QUERY_LOG_GENERAL - or QUERY_LOG_SLOW - lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. + log_table_type type of the log table to close: QUERY_LOG_GENERAL + or QUERY_LOG_SLOW + lock_in_use Set to TRUE if the caller owns LOCK_open. FALSE otherwise. DESCRIPTION @@ -1094,7 +1094,7 @@ int LOGGER::set_handlers(uint error_log_printer, */ void Log_to_csv_event_handler:: - close_log_table(uint log_type, bool lock_in_use) + close_log_table(uint log_table_type, bool lock_in_use) { THD *log_thd, *curr= current_thd; TABLE_LIST *table; @@ -1102,7 +1102,7 @@ void Log_to_csv_event_handler:: if (!logger.is_log_tables_initialized) return; /* do nothing */ - switch (log_type) { + switch (log_table_type) { case QUERY_LOG_GENERAL: log_thd= general_log_thd; table= &general_log; @@ -1143,7 +1143,7 @@ void Log_to_csv_event_handler:: /* this function is mostly a placeholder. - conceptually, binlog initialization (now mostly done in MYSQL_LOG::open) + conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open) should be moved here. */ @@ -1232,7 +1232,7 @@ static int binlog_prepare(THD *thd, bool all) do nothing. just pretend we can do 2pc, so that MySQL won't switch to 1pc. - real work will be done in MYSQL_LOG::log() + real work will be done in MYSQL_BIN_LOG::log() */ return 0; } @@ -1248,7 +1248,7 @@ static int binlog_commit(THD *thd, bool all) if (trx_data->empty()) { - // we're here because trans_log was flushed in MYSQL_LOG::log() + // we're here because trans_log was flushed in MYSQL_BIN_LOG::log() DBUG_RETURN(0); } Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); @@ -1488,12 +1488,119 @@ static int find_uniq_filename(char *name) } +void MYSQL_LOG::init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg) +{ + DBUG_ENTER("MYSQL_LOG::init"); + log_type= log_type_arg; + io_cache_type= io_cache_type_arg; + DBUG_PRINT("info",("log_type: %d", log_type)); + DBUG_VOID_RETURN; +} + + +/* + Open a (new) log file. + + SYNOPSIS + open() + + log_name The name of the log to open + log_type_arg The type of the log. E.g. LOG_NORMAL + new_name The new name for the logfile. This is only needed + when the method is used to open the binlog file. + io_cache_type_arg The type of the IO_CACHE to use for this log file + + DESCRIPTION + Open the logfile, init IO_CACHE and write startup messages + (in case of general and slow query logs). + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, + const char *new_name, enum cache_type io_cache_type_arg) +{ + char buff[FN_REFLEN]; + File file= -1; + int open_flags= O_CREAT | O_BINARY; + DBUG_ENTER("MYSQL_LOG::open"); + DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg)); + + write_error= 0; + + init(log_type_arg, io_cache_type_arg); + + if (!(name= my_strdup(log_name, MYF(MY_WME)))) + { + name= (char *)log_name; // for the error message + goto err; + } + + if (new_name) + strmov(log_file_name, new_name); + else if (generate_new_name(log_file_name, name)) + goto err; + + if (io_cache_type == SEQ_READ_APPEND) + open_flags |= O_RDWR | O_APPEND; + else + open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND); + + db[0]= 0; + + if ((file= my_open(log_file_name, open_flags, + MYF(MY_WME | ME_WAITTANG))) < 0 || + init_io_cache(&log_file, file, IO_SIZE, io_cache_type, + my_tell(file, MYF(MY_WME)), 0, + MYF(MY_WME | MY_NABP | + ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) + goto err; + + if (log_type == LOG_NORMAL) + { + char *end; + int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " +#ifdef EMBEDDED_LIBRARY + "embedded library\n", my_progname, server_version +#elif __NT__ + "started with:\nTCP Port: %d, Named Pipe: %s\n", + my_progname, server_version, mysqld_port, + mysqld_unix_port +#else + "started with:\nTcp port: %d Unix socket: %s\n", + my_progname, server_version, mysqld_port, + mysqld_unix_port +#endif + ); + end= strnmov(buff + len, "Time Id Command Argument\n", + sizeof(buff) - len); + if (my_b_write(&log_file, (byte*) buff, (uint) (end-buff)) || + flush_io_cache(&log_file)) + goto err; + } + + log_state= LOG_OPENED; + DBUG_RETURN(0); + +err: + sql_print_error("Could not use %s for logging (error %d). \ +Turning logging off for the whole duration of the MySQL server process. \ +To turn it on again: fix the cause, \ +shutdown the MySQL server and restart it.", name, errno); + if (file >= 0) + my_close(file, MYF(0)); + end_io_cache(&log_file); + safeFree(name); + log_state= LOG_CLOSED; + DBUG_RETURN(1); +} + MYSQL_LOG::MYSQL_LOG() - :bytes_written(0), last_time(0), query_start(0), name(0), - prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1), - write_error(FALSE), inited(FALSE), need_start_event(TRUE), - m_table_map_version(0), - description_event_for_exec(0), description_event_for_queue(0) + : name(0), log_type(LOG_UNKNOWN), log_state(LOG_CLOSED), write_error(FALSE), + inited(FALSE) { /* We don't want to initialize LOCK_Log here as such initialization depends on @@ -1501,14 +1608,412 @@ MYSQL_LOG::MYSQL_LOG() called only in main(). Doing initialization here would make it happen before main(). */ - index_file_name[0] = 0; - bzero((char*) &log_file,sizeof(log_file)); - bzero((char*) &index_file, sizeof(index_file)); + bzero((char*) &log_file, sizeof(log_file)); +} + +void MYSQL_LOG::init_pthread_objects() +{ + DBUG_ASSERT(inited == 0); + inited= 1; + (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW); +} + +/* + Close the log file + + SYNOPSIS + close() + exiting Bitmask. For the slow and general logs the only used bit is + LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call + open at once after close. + + NOTES + One can do an open on the object at once after doing a close. + The internal structures are not freed until cleanup() is called +*/ + +void MYSQL_LOG::close(uint exiting) +{ // One can't set log_type here! + DBUG_ENTER("MYSQL_LOG::close"); + DBUG_PRINT("enter",("exiting: %d", (int) exiting)); + if (log_state == LOG_OPENED) + { + end_io_cache(&log_file); + + if (my_sync(log_file.file, MYF(MY_WME)) && ! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + + if (my_close(log_file.file, MYF(MY_WME)) && ! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + } + + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; + safeFree(name); + DBUG_VOID_RETURN; } /* this is called only once */ void MYSQL_LOG::cleanup() +{ + DBUG_ENTER("cleanup"); + if (inited) + { + inited= 0; + (void) pthread_mutex_destroy(&LOCK_log); + close(0); + } + DBUG_VOID_RETURN; +} + + +int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) +{ + fn_format(new_name, log_name, mysql_data_home, "", 4); + if (log_type == LOG_BIN) + { + if (!fn_ext(log_name)[0]) + { + if (find_uniq_filename(new_name)) + { + sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); + return 1; + } + } + } + return 0; +} + + +/* + Reopen the log file + + SYNOPSIS + reopen_file() + + DESCRIPTION + Reopen the log file. The method is used during FLUSH LOGS + and locks LOCK_log mutex +*/ + + +void MYSQL_QUERY_LOG::reopen_file() +{ + char *save_name; + + DBUG_ENTER("MYSQL_LOG::reopen_file"); + if (!is_open()) + { + DBUG_PRINT("info",("log is closed")); + DBUG_VOID_RETURN; + } + + pthread_mutex_lock(&LOCK_log); + + save_name= name; + name= 0; // Don't free name + close(LOG_CLOSE_TO_BE_OPENED); + + /* + Note that at this point, log_state != LOG_CLOSED (important for is_open()). + */ + + open(save_name, log_type, 0, io_cache_type); + my_free(save_name, MYF(0)); + + pthread_mutex_unlock(&LOCK_log); + + DBUG_VOID_RETURN; +} + + +/* + Write a command to traditional general log file + + SYNOPSIS + write() + + event_time command start timestamp + user_host the pointer to the string with user@host info + user_host_len length of the user_host string. this is computed once + and passed to all general log event handlers + thread_id Id of the thread, issued a query + command_type the type of the command being logged + command_type_len the length of the string above + sql_text the very text of the query being executed + sql_text_len the length of sql_text string + + DESCRIPTION + + Log given command to to normal (not rotable) log file + + RETURN + FASE - OK + TRUE - error occured +*/ + +bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host, + uint user_host_len, int thread_id, + const char *command_type, uint command_type_len, + const char *sql_text, uint sql_text_len) +{ + char buff[32]; + uint length= 0; + char time_buff[MAX_TIME_SIZE]; + struct tm start; + uint time_buff_len= 0; + + /* Test if someone closed between the is_open test and lock */ + if (is_open()) + { + /* Note that my_b_write() assumes it knows the length for this */ + if (event_time != last_time) + { + last_time= event_time; + + localtime_r(&event_time, &start); + + time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE, + "%02d%02d%02d %2d:%02d:%02d", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + + if (my_b_write(&log_file, (byte*) &time_buff, time_buff_len)) + goto err; + } + else + if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0) + goto err; + + /* command_type, thread_id */ + length= my_snprintf(buff, 32, "%5ld ", thread_id); + + if (my_b_write(&log_file, (byte*) buff, length)) + goto err; + + if (my_b_write(&log_file, (byte*) command_type, command_type_len)) + goto err; + + if (my_b_write(&log_file, (byte*) "\t", 1)) + goto err; + + /* sql_text */ + if (my_b_write(&log_file, (byte*) sql_text, sql_text_len)) + goto err; + + if (my_b_write(&log_file, (byte*) "\n", 1) || + flush_io_cache(&log_file)) + goto err; + } + + return FALSE; +err: + + if (!write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } + return TRUE; +} + + +/* + Log a query to the traditional slow log file + + SYNOPSIS + write() + + thd THD of the query + current_time current timestamp + query_start_arg command start timestamp + user_host the pointer to the string with user@host info + user_host_len length of the user_host string. this is computed once + and passed to all general log event handlers + query_time Amount of time the query took to execute (in seconds) + lock_time Amount of time the query was locked (in seconds) + is_command The flag, which determines, whether the sql_text is a + query or an administrator command. + sql_text the very text of the query or administrator command + processed + sql_text_len the length of sql_text string + + DESCRIPTION + + Log a query to the slow log file. + + RETURN + FALSE - OK + TRUE - error occured +*/ + +bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, + time_t query_start_arg, const char *user_host, + uint user_host_len, longlong query_time, + longlong lock_time, bool is_command, + const char *sql_text, uint sql_text_len) +{ + bool error= 0; + DBUG_ENTER("MYSQL_QUERY_LOG::write"); + + if (!is_open()) + DBUG_RETURN(0); + + if (is_open()) + { // Safety agains reopen + int tmp_errno= 0; + char buff[80], *end; + uint buff_len; + end= buff; + + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) + { + Security_context *sctx= thd->security_ctx; + if (current_time != last_time) + { + last_time= current_time; + struct tm start; + localtime_r(¤t_time, &start); + + buff_len= my_snprintf(buff, sizeof buff, + "# Time: %02d%02d%02d %2d:%02d:%02d\n", + start.tm_year % 100, start.tm_mon + 1, + start.tm_mday, start.tm_hour, + start.tm_min, start.tm_sec); + + /* Note that my_b_write() assumes it knows the length for this */ + if (my_b_write(&log_file, (byte*) buff, buff_len)) + tmp_errno= errno; + } + if (my_b_printf(&log_file, "# User@Host: ", sizeof("# User@Host: ") - 1) + != sizeof("# User@Host: ") - 1) + tmp_errno= errno; + if (my_b_printf(&log_file, user_host, user_host_len) != user_host_len) + tmp_errno= errno; + if (my_b_write(&log_file, (byte*) "\n", 1)) + tmp_errno= errno; + } + /* For slow query log */ + if (my_b_printf(&log_file, + "# Query_time: %lu Lock_time: %lu" + " Rows_sent: %lu Rows_examined: %lu\n", + (ulong) query_time, (ulong) lock_time, + (ulong) thd->sent_row_count, + (ulong) thd->examined_row_count) == (uint) -1) + tmp_errno= errno; + if (thd->db && strcmp(thd->db, db)) + { // Database changed + if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1) + tmp_errno= errno; + strmov(db,thd->db); + } + if (thd->last_insert_id_used) + { + end=strmov(end, ",last_insert_id="); + end=longlong10_to_str((longlong) thd->current_insert_id, end, -10); + } + // Save value if we do an insert. + if (thd->insert_id_used) + { + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) + { + end=strmov(end,",insert_id="); + end=longlong10_to_str((longlong) thd->last_insert_id, end, -10); + } + } + + /* + This info used to show up randomly, depending on whether the query + checked the query start time or not. now we always write current + timestamp to the slow log + */ + end= strmov(end, ",timestamp="); + end= int10_to_str((long) current_time, end, 10); + + if (end != buff) + { + *end++=';'; + *end='\n'; + if (my_b_write(&log_file, (byte*) "SET ", 4) || + my_b_write(&log_file, (byte*) buff + 1, (uint) (end-buff))) + tmp_errno= errno; + } + if (is_command) + { + end= strxmov(buff, "# administrator command: ", NullS); + buff_len= (ulong) (end - buff); + my_b_write(&log_file, (byte*) buff, buff_len); + } + if (my_b_write(&log_file, (byte*) sql_text, sql_text_len) || + my_b_write(&log_file, (byte*) ";\n",2) || + flush_io_cache(&log_file)) + tmp_errno= errno; + if (tmp_errno) + { + error= 1; + if (! write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, error); + } + } + } + DBUG_RETURN(error); +} + + +const char *MYSQL_LOG::generate_name(const char *log_name, + const char *suffix, + bool strip_ext, char *buff) +{ + if (!log_name || !log_name[0]) + { + /* + TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. + */ + strmake(buff, glob_hostname, FN_REFLEN - 5); + strmov(fn_ext(buff), suffix); + return (const char *)buff; + } + // get rid of extension if the log is binary to avoid problems + if (strip_ext) + { + char *p= fn_ext(log_name); + uint length= (uint) (p - log_name); + strmake(buff, log_name, min(length, FN_REFLEN)); + return (const char*)buff; + } + return log_name; +} + + + +MYSQL_BIN_LOG::MYSQL_BIN_LOG() + :bytes_written(0), prepared_xids(0), file_id(1), open_count(1), + need_start_event(TRUE), m_table_map_version(0), + description_event_for_exec(0), description_event_for_queue(0) +{ + /* + We don't want to initialize locks here as such initialization depends on + safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is + called only in main(). Doing initialization here would make it happen + before main(). + */ + index_file_name[0] = 0; + bzero((char*) &index_file, sizeof(index_file)); +} + +/* this is called only once */ + +void MYSQL_BIN_LOG::cleanup() { DBUG_ENTER("cleanup"); if (inited) @@ -1525,75 +2030,28 @@ void MYSQL_LOG::cleanup() } -int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) +/* Init binlog-specific vars */ +void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg) { - fn_format(new_name,log_name,mysql_data_home,"",4); - if (log_type != LOG_NORMAL) - { - if (!fn_ext(log_name)[0]) - { - if (find_uniq_filename(new_name)) - { - sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); - return 1; - } - } - } - return 0; -} - - -void MYSQL_LOG::init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, - ulong max_size_arg) -{ - DBUG_ENTER("MYSQL_LOG::init"); - log_type = log_type_arg; - io_cache_type = io_cache_type_arg; - no_auto_events = no_auto_events_arg; - max_size=max_size_arg; - DBUG_PRINT("info",("log_type: %d max_size: %lu", log_type, max_size)); + DBUG_ENTER("MYSQL_BIN_LOG::init"); + no_auto_events= no_auto_events_arg; + max_size= max_size_arg; + DBUG_PRINT("info",("max_size: %lu", max_size)); DBUG_VOID_RETURN; } -void MYSQL_LOG::init_pthread_objects() +void MYSQL_BIN_LOG::init_pthread_objects() { DBUG_ASSERT(inited == 0); inited= 1; - (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_log, MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); (void) pthread_cond_init(&update_cond, 0); } -const char *MYSQL_LOG::generate_name(const char *log_name, - const char *suffix, - bool strip_ext, char *buff) -{ - if (!log_name || !log_name[0]) - { - /* - TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. - */ - strmake(buff,glob_hostname,FN_REFLEN-5); - strmov(fn_ext(buff),suffix); - return (const char *)buff; - } - // get rid of extension if the log is binary to avoid problems - if (strip_ext) - { - char *p = fn_ext(log_name); - uint length=(uint) (p-log_name); - strmake(buff,log_name,min(length,FN_REFLEN)); - return (const char*)buff; - } - return log_name; -} - -bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, +bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg, const char *log_name) { File index_file_nr= -1; @@ -1630,10 +2088,10 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, /* - Open a (new) log file. + Open a (new) binlog file. DESCRIPTION - - If binary logs, also open the index file and register the new + - Open the log file and the index file. Register the new file name in it - When calling this when the file is in use, you must have a locks on LOCK_log and LOCK_index. @@ -1643,94 +2101,32 @@ bool MYSQL_LOG::open_index_file(const char *index_file_name_arg, 1 error */ -bool MYSQL_LOG::open(const char *log_name, - enum_log_type log_type_arg, - const char *new_name, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, - ulong max_size_arg, - bool null_created_arg) +bool MYSQL_BIN_LOG::open(const char *log_name, + enum_log_type log_type_arg, + const char *new_name, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg, + ulong max_size_arg, + bool null_created_arg) { char buff[FN_REFLEN]; File file= -1; int open_flags = O_CREAT | O_BINARY; - DBUG_ENTER("MYSQL_LOG::open"); + DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); - last_time=query_start=0; write_error=0; - init(log_type_arg,io_cache_type_arg,no_auto_events_arg,max_size_arg); + /* open the main log file */ + if (MYSQL_LOG::open(log_name, log_type_arg, new_name, io_cache_type_arg)) + DBUG_RETURN(1); /* all warnings issued */ - if (!(name=my_strdup(log_name,MYF(MY_WME)))) - { - name= (char *)log_name; // for the error message - goto err; - } - if (new_name) - strmov(log_file_name,new_name); - else if (generate_new_name(log_file_name, name)) - goto err; + init(no_auto_events_arg, max_size_arg); - if (io_cache_type == SEQ_READ_APPEND) - open_flags |= O_RDWR | O_APPEND; - else - open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND); - - db[0]=0; open_count++; - if ((file=my_open(log_file_name,open_flags, - MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, io_cache_type, - my_tell(file,MYF(MY_WME)), 0, - MYF(MY_WME | MY_NABP | - ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) - goto err; - switch (log_type) { - case LOG_NORMAL: - { - char *end; - int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s. " -#ifdef EMBEDDED_LIBRARY - "embedded library\n", my_progname, server_version -#elif __NT__ - "started with:\nTCP Port: %d, Named Pipe: %s\n", - my_progname, server_version, mysqld_port, mysqld_unix_port -#else - "started with:\nTcp port: %d Unix socket: %s\n", - my_progname,server_version,mysqld_port,mysqld_unix_port -#endif - ); - end=strnmov(buff+len,"Time Id Command Argument\n", - sizeof(buff)-len); - if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) || - flush_io_cache(&log_file)) - goto err; - break; - } - case LOG_NEW: - { - uint len; - time_t skr=time(NULL); - struct tm tm_tmp; + DBUG_ASSERT(log_type == LOG_BIN); - localtime_r(&skr,&tm_tmp); - len= my_snprintf(buff,sizeof(buff), - "# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n", - my_progname,server_version, - tm_tmp.tm_year % 100, - tm_tmp.tm_mon+1, - tm_tmp.tm_mday, - tm_tmp.tm_hour, - tm_tmp.tm_min, - tm_tmp.tm_sec); - if (my_b_write(&log_file, (byte*) buff, len) || - flush_io_cache(&log_file)) - goto err; - break; - } - case LOG_BIN: { bool write_file_name_to_index_file=0; @@ -1821,13 +2217,9 @@ bool MYSQL_LOG::open(const char *log_name, my_sync(index_file.file, MYF(MY_WME))) goto err; } - break; - } - case LOG_CLOSED: // Impossible - case LOG_TO_BE_OPENED: - DBUG_ASSERT(1); - break; } + log_state= LOG_OPENED; + DBUG_RETURN(0); err: @@ -1840,12 +2232,12 @@ shutdown the MySQL server and restart it.", name, errno); end_io_cache(&log_file); end_io_cache(&index_file); safeFree(name); - log_type= LOG_CLOSED; + log_state= LOG_CLOSED; DBUG_RETURN(1); } -int MYSQL_LOG::get_current_log(LOG_INFO* linfo) +int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo) { pthread_mutex_lock(&LOCK_log); strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1); @@ -1932,7 +2324,7 @@ err: LOG_INFO_IO Got IO error while reading file */ -int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, +int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, bool need_lock) { int error= 0; @@ -2006,7 +2398,7 @@ int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, LOG_INFO_IO Got IO error while reading file */ -int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) +int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { int error= 0; uint length; @@ -2054,12 +2446,11 @@ err: 1 error */ -bool MYSQL_LOG::reset_logs(THD* thd) +bool MYSQL_BIN_LOG::reset_logs(THD* thd) { LOG_INFO linfo; bool error=0; const char* save_name; - enum_log_type save_log_type; DBUG_ENTER("reset_logs"); ha_reset_logs(thd); @@ -2081,7 +2472,6 @@ bool MYSQL_LOG::reset_logs(THD* thd) /* Save variables so that we can reopen the log */ save_name=name; name=0; // Protect against free - save_log_type=log_type; close(LOG_CLOSE_TO_BE_OPENED); /* First delete all old log files */ @@ -2105,8 +2495,7 @@ bool MYSQL_LOG::reset_logs(THD* thd) if (!thd->slave_thread) need_start_event=1; if (!open_index_file(index_file_name, 0)) - open(save_name, save_log_type, 0, - io_cache_type, no_auto_events, max_size, 0); + open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0); my_free((gptr) save_name, MYF(0)); err: @@ -2154,7 +2543,7 @@ err: #ifdef HAVE_REPLICATION -int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) +int MYSQL_BIN_LOG::purge_first_log(struct st_relay_log_info* rli, bool included) { int error; DBUG_ENTER("purge_first_log"); @@ -2230,7 +2619,7 @@ err: Update log index_file */ -int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) +int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) { if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset)) return LOG_INFO_IO; @@ -2263,7 +2652,7 @@ int MYSQL_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads) LOG_INFO_EOF to_log not found */ -int MYSQL_LOG::purge_logs(const char *to_log, +int MYSQL_BIN_LOG::purge_logs(const char *to_log, bool included, bool need_mutex, bool need_update_threads, @@ -2349,7 +2738,7 @@ err: LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated */ -int MYSQL_LOG::purge_logs_before_date(time_t purge_time) +int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time) { int error; LOG_INFO log_info; @@ -2408,7 +2797,7 @@ err: If file name will be longer then FN_REFLEN it will be truncated */ -void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) +void MYSQL_BIN_LOG::make_log_name(char* buf, const char* log_ident) { uint dir_len = dirname_length(log_file_name); if (dir_len > FN_REFLEN) @@ -2422,29 +2811,48 @@ void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) Check if we are writing/reading to the given log file */ -bool MYSQL_LOG::is_active(const char *log_file_name_arg) +bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg) { return !strcmp(log_file_name, log_file_name_arg); } +/* + Wrappers around new_file_impl to avoid using argument + to control locking. The argument 1) less readable 2) breaks + incapsulation 3) allows external access to the class without + a lock (which is not possible with private new_file_without_locking + method). +*/ + +void MYSQL_BIN_LOG::new_file() +{ + new_file_impl(1); +} + + +void MYSQL_BIN_LOG::new_file_without_locking() +{ + new_file_impl(0); +} + + /* Start writing to a new log file or reopen the old file SYNOPSIS - new_file() + new_file_impl() need_lock Set to 1 if caller has not locked LOCK_log NOTE The new file name is stored last in the index file */ -void MYSQL_LOG::new_file(bool need_lock) +void MYSQL_BIN_LOG::new_file_impl(bool need_lock) { char new_name[FN_REFLEN], *new_name_ptr, *old_name; - enum_log_type save_log_type; - DBUG_ENTER("MYSQL_LOG::new_file"); + DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) { DBUG_PRINT("info",("log is closed")); @@ -2510,12 +2918,11 @@ void MYSQL_LOG::new_file(bool need_lock) signal_update(); } old_name=name; - save_log_type=log_type; name=0; // Don't free name close(LOG_CLOSE_TO_BE_OPENED); /* - Note that at this point, log_type != LOG_CLOSED (important for is_open()). + Note that at this point, log_state != LOG_CLOSED (important for is_open()). */ /* @@ -2527,7 +2934,7 @@ void MYSQL_LOG::new_file(bool need_lock) trigger temp tables deletion on slaves. */ - open(old_name, save_log_type, new_name_ptr, + open(old_name, log_type, new_name_ptr, io_cache_type, no_auto_events, max_size, 1); my_free(old_name,MYF(0)); @@ -2540,11 +2947,11 @@ end: } -bool MYSQL_LOG::append(Log_event* ev) +bool MYSQL_BIN_LOG::append(Log_event* ev) { bool error = 0; pthread_mutex_lock(&LOCK_log); - DBUG_ENTER("MYSQL_LOG::append"); + DBUG_ENTER("MYSQL_BIN_LOG::append"); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); /* @@ -2559,7 +2966,7 @@ bool MYSQL_LOG::append(Log_event* ev) bytes_written+= ev->data_written; DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file(0); + new_file_without_locking(); err: pthread_mutex_unlock(&LOCK_log); @@ -2568,10 +2975,10 @@ err: } -bool MYSQL_LOG::appendv(const char* buf, uint len,...) +bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) { bool error= 0; - DBUG_ENTER("MYSQL_LOG::appendv"); + DBUG_ENTER("MYSQL_BIN_LOG::appendv"); va_list(args); va_start(args,len); @@ -2589,7 +2996,7 @@ bool MYSQL_LOG::appendv(const char* buf, uint len,...) } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file(0); + new_file_without_locking(); err: if (!error) @@ -2598,99 +3005,7 @@ err: } -/* - Write a command to traditional general log file - - SYNOPSIS - write() - - event_time command start timestamp - user_host the pointer to the string with user@host info - user_host_len length of the user_host string. this is computed once - and passed to all general log event handlers - thread_id Id of the thread, issued a query - command_type the type of the command being logged - command_type_len the length of the string above - sql_text the very text of the query being executed - sql_text_len the length of sql_text string - - DESCRIPTION - - Log given command to to normal (not rotable) log file - - RETURN - FASE - OK - TRUE - error occured -*/ - -bool MYSQL_LOG::write(time_t event_time, const char *user_host, - uint user_host_len, int thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len) -{ - char buff[32]; - uint length= 0; - char time_buff[MAX_TIME_SIZE]; - struct tm start; - uint time_buff_len= 0; - - /* Test if someone closed between the is_open test and lock */ - if (is_open()) - { - /* Note that my_b_write() assumes it knows the length for this */ - if (event_time != last_time) - { - last_time= event_time; - - localtime_r(&event_time, &start); - - time_buff_len= my_snprintf(time_buff, MAX_TIME_SIZE, - "%02d%02d%02d %2d:%02d:%02d", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); - - if (my_b_write(&log_file, (byte*) &time_buff, time_buff_len)) - goto err; - } - else - if (my_b_write(&log_file, (byte*) "\t\t" ,2) < 0) - goto err; - - /* command_type, thread_id */ - length= my_snprintf(buff, 32, "%5ld ", thread_id); - - if (my_b_write(&log_file, (byte*) buff, length)) - goto err; - - if (my_b_write(&log_file, (byte*) command_type, command_type_len)) - goto err; - - if (my_b_write(&log_file, (byte*) "\t", 1)) - goto err; - - /* sql_text */ - if (my_b_write(&log_file, (byte*) sql_text, sql_text_len)) - goto err; - - if (my_b_write(&log_file, (byte*) "\n", 1) || - flush_io_cache(&log_file)) - goto err; - } - - return FALSE; -err: - - if (!write_error) - { - write_error= 1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } - return TRUE; -} - - -bool MYSQL_LOG::flush_and_sync() +bool MYSQL_BIN_LOG::flush_and_sync() { int err=0, fd=log_file.file; safe_mutex_assert_owner(&LOCK_log); @@ -2704,7 +3019,7 @@ bool MYSQL_LOG::flush_and_sync() return err; } -void MYSQL_LOG::start_union_events(THD *thd) +void MYSQL_BIN_LOG::start_union_events(THD *thd) { DBUG_ASSERT(!thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= TRUE; @@ -2713,13 +3028,13 @@ void MYSQL_LOG::start_union_events(THD *thd) thd->binlog_evt_union.first_query_id= thd->query_id; } -void MYSQL_LOG::stop_union_events(THD *thd) +void MYSQL_BIN_LOG::stop_union_events(THD *thd) { DBUG_ASSERT(thd->binlog_evt_union.do_union); thd->binlog_evt_union.do_union= FALSE; } -bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) +bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) { return (thd->binlog_evt_union.do_union && query_id_param >= thd->binlog_evt_union.first_query_id); @@ -2826,9 +3141,10 @@ THD::binlog_set_pending_rows_event(Rows_log_event* ev) (either cached binlog if transaction, or disk binlog). Sets a new pending event. */ -int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) +int MYSQL_BIN_LOG:: + flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) { - DBUG_ENTER("MYSQL_LOG::flush_and_set_pending_rows_event(event)"); + DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)"); DBUG_ASSERT(thd->current_stmt_binlog_row_based && mysql_bin_log.is_open()); DBUG_PRINT("enter", ("event=%p", event)); @@ -2912,11 +3228,11 @@ int MYSQL_LOG::flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event) Write an event to the binary log */ -bool MYSQL_LOG::write(Log_event *event_info) +bool MYSQL_BIN_LOG::write(Log_event *event_info) { THD *thd= event_info->thd; bool error= 1; - DBUG_ENTER("MYSQL_LOG::write(Log_event *)"); + DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)"); if (thd->binlog_evt_union.do_union) { @@ -3136,14 +3452,14 @@ bool general_log_print(THD *thd, enum enum_server_command command, return error; } -void MYSQL_LOG::rotate_and_purge(uint flags) +void MYSQL_BIN_LOG::rotate_and_purge(uint flags) { if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED)) pthread_mutex_lock(&LOCK_log); if ((flags & RP_FORCE_ROTATE) || (my_b_tell(&log_file) >= (my_off_t) max_size)) { - new_file(0); + new_file_without_locking(); #ifdef HAVE_REPLICATION if (expire_logs_days) { @@ -3157,7 +3473,7 @@ void MYSQL_LOG::rotate_and_purge(uint flags) pthread_mutex_unlock(&LOCK_log); } -uint MYSQL_LOG::next_file_id() +uint MYSQL_BIN_LOG::next_file_id() { uint res; pthread_mutex_lock(&LOCK_log); @@ -3188,9 +3504,9 @@ uint MYSQL_LOG::next_file_id() that the same updates are run on the slave. */ -bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) +bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) { - DBUG_ENTER("MYSQL_LOG::write(THD *, IO_CACHE *, Log_event *)"); + DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)"); VOID(pthread_mutex_lock(&LOCK_log)); if (likely(is_open())) // Should always be true @@ -3285,148 +3601,6 @@ err: } -/* - Log a query to the traditional slow log file - - SYNOPSIS - write() - - thd THD of the query - current_time current timestamp - query_start_arg command start timestamp - user_host the pointer to the string with user@host info - user_host_len length of the user_host string. this is computed once - and passed to all general log event handlers - query_time Amount of time the query took to execute (in seconds) - lock_time Amount of time the query was locked (in seconds) - is_command The flag, which determines, whether the sql_text is a - query or an administrator command. - sql_text the very text of the query or administrator command - processed - sql_text_len the length of sql_text string - - DESCRIPTION - - Log a query to the slow log file. - - RETURN - FALSE - OK - TRUE - error occured -*/ - -bool MYSQL_LOG::write(THD *thd, time_t current_time, time_t query_start_arg, - const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, - const char *sql_text, uint sql_text_len) -{ - bool error= 0; - DBUG_ENTER("MYSQL_LOG::write"); - - if (!is_open()) - DBUG_RETURN(0); - - if (is_open()) - { // Safety agains reopen - int tmp_errno= 0; - char buff[80], *end; - uint buff_len; - end= buff; - - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) - { - Security_context *sctx= thd->security_ctx; - if (current_time != last_time) - { - last_time= current_time; - struct tm start; - localtime_r(¤t_time, &start); - - buff_len= my_snprintf(buff, sizeof buff, - "# Time: %02d%02d%02d %2d:%02d:%02d\n", - start.tm_year % 100, start.tm_mon + 1, - start.tm_mday, start.tm_hour, - start.tm_min, start.tm_sec); - - /* Note that my_b_write() assumes it knows the length for this */ - if (my_b_write(&log_file, (byte*) buff, buff_len)) - tmp_errno=errno; - } - if (my_b_printf(&log_file, "# User@Host: ", sizeof("# User@Host: ") - 1)) - tmp_errno=errno; - if (my_b_printf(&log_file, user_host, user_host_len)) - tmp_errno=errno; - if (my_b_write(&log_file, (byte*) "\n", 1)) - tmp_errno=errno; - } - /* For slow query log */ - if (my_b_printf(&log_file, - "# Query_time: %lu Lock_time: %lu" - " Rows_sent: %lu Rows_examined: %lu\n", - (ulong) query_time, (ulong) lock_time, - (ulong) thd->sent_row_count, - (ulong) thd->examined_row_count) == (uint) -1) - tmp_errno=errno; - if (thd->db && strcmp(thd->db,db)) - { // Database changed - if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1) - tmp_errno=errno; - strmov(db,thd->db); - } - if (thd->last_insert_id_used) - { - end=strmov(end,",last_insert_id="); - end=longlong10_to_str((longlong) thd->current_insert_id,end,-10); - } - // Save value if we do an insert. - if (thd->insert_id_used) - { - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT)) - { - end=strmov(end,",insert_id="); - end=longlong10_to_str((longlong) thd->last_insert_id,end,-10); - } - } - - /* - This info used to show up randomly, depending on whether the query - checked the query start time or not. now we always write current - timestamp to the slow log - */ - end= strmov(end, ",timestamp="); - end= int10_to_str((long) current_time, end, 10); - - if (end != buff) - { - *end++=';'; - *end='\n'; - if (my_b_write(&log_file, (byte*) "SET ",4) || - my_b_write(&log_file, (byte*) buff+1,(uint) (end-buff))) - tmp_errno=errno; - } - if (is_command) - { - end= strxmov(buff, "# administrator command: ", NullS); - buff_len= (ulong) (end - buff); - my_b_write(&log_file, (byte*) buff, buff_len); - } - if (my_b_write(&log_file, (byte*) sql_text, sql_text_len) || - my_b_write(&log_file, (byte*) ";\n",2) || - flush_io_cache(&log_file)) - tmp_errno=errno; - if (tmp_errno) - { - error=1; - if (! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,error); - } - } - } - DBUG_RETURN(error); -} - - /* Wait until we get a signal that the binary log has been updated @@ -3443,7 +3617,7 @@ bool MYSQL_LOG::write(THD *thd, time_t current_time, time_t query_start_arg, THD::enter_cond() (see NOTES in sql_class.h). */ -void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave) +void MYSQL_BIN_LOG::wait_for_update(THD* thd, bool is_slave) { const char *old_msg; DBUG_ENTER("wait_for_update"); @@ -3476,11 +3650,11 @@ void MYSQL_LOG::wait_for_update(THD* thd, bool is_slave) The internal structures are not freed until cleanup() is called */ -void MYSQL_LOG::close(uint exiting) +void MYSQL_BIN_LOG::close(uint exiting) { // One can't set log_type here! - DBUG_ENTER("MYSQL_LOG::close"); + DBUG_ENTER("MYSQL_BIN_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); - if (log_type != LOG_CLOSED && log_type != LOG_TO_BE_OPENED) + if (log_state == LOG_OPENED) { #ifdef HAVE_REPLICATION if (log_type == LOG_BIN && !no_auto_events && @@ -3492,7 +3666,6 @@ void MYSQL_LOG::close(uint exiting) signal_update(); } #endif /* HAVE_REPLICATION */ - end_io_cache(&log_file); /* don't pwrite in a file opened with O_APPEND - it doesn't work */ if (log_file.type == WRITE_CACHE && log_type == LOG_BIN) @@ -3502,16 +3675,8 @@ void MYSQL_LOG::close(uint exiting) my_pwrite(log_file.file, &flags, 1, offset, MYF(0)); } - if (my_sync(log_file.file,MYF(MY_WME)) && ! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } - if (my_close(log_file.file,MYF(MY_WME)) && ! write_error) - { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - } + /* this will cleanup IO_CACHE, sync and close the file */ + MYSQL_LOG::close(exiting); } /* @@ -3528,13 +3693,13 @@ void MYSQL_LOG::close(uint exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno); } } - log_type= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED : LOG_CLOSED; safeFree(name); DBUG_VOID_RETURN; } -void MYSQL_LOG::set_max_size(ulong max_size_arg) +void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg) { /* We need to take locks, otherwise this may happen: @@ -3543,7 +3708,7 @@ void MYSQL_LOG::set_max_size(ulong max_size_arg) uses the old_max_size argument, so max_size_arg has been overwritten and it's like if the SET command was never run. */ - DBUG_ENTER("MYSQL_LOG::set_max_size"); + DBUG_ENTER("MYSQL_BIN_LOG::set_max_size"); pthread_mutex_lock(&LOCK_log); if (is_open()) max_size= max_size_arg; @@ -3692,9 +3857,9 @@ bool flush_error_log() return result; } -void MYSQL_LOG::signal_update() +void MYSQL_BIN_LOG::signal_update() { - DBUG_ENTER("MYSQL_LOG::signal_update"); + DBUG_ENTER("MYSQL_BIN_LOG::signal_update"); pthread_cond_broadcast(&update_cond); DBUG_VOID_RETURN; } @@ -4297,7 +4462,7 @@ int TC_LOG::using_heuristic_recover() } /****** transaction coordinator log for 2pc - binlog() based solution ******/ -#define TC_LOG_BINLOG MYSQL_LOG +#define TC_LOG_BINLOG MYSQL_BIN_LOG /* TODO keep in-memory list of prepared transactions diff --git a/sql/log.h b/sql/log.h index 49aae15feee..80992a7910a 100644 --- a/sql/log.h +++ b/sql/log.h @@ -147,33 +147,85 @@ typedef struct st_log_info class Log_event; class Rows_log_event; -enum enum_log_type { LOG_CLOSED, LOG_TO_BE_OPENED, LOG_NORMAL, LOG_NEW, LOG_BIN}; +enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN }; +enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED }; /* - TODO split MYSQL_LOG into base MYSQL_LOG and - MYSQL_QUERY_LOG, MYSQL_SLOW_LOG, MYSQL_BIN_LOG - most of the code from MYSQL_LOG should be in the MYSQL_BIN_LOG - only (TC_LOG included) - TODO use mmap instead of IO_CACHE for binlog (mmap+fsync is two times faster than write+fsync) */ -class MYSQL_LOG: public TC_LOG +class MYSQL_LOG +{ +public: + MYSQL_LOG(); + void init_pthread_objects(); + void cleanup(); + bool open(const char *log_name, + enum_log_type log_type, + const char *new_name, + enum cache_type io_cache_type_arg); + void init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg); + void close(uint exiting); + inline bool is_open() { return log_state != LOG_CLOSED; } + const char *generate_name(const char *log_name, const char *suffix, + bool strip_ext, char *buff); + int generate_new_name(char *new_name, const char *log_name); + protected: + /* LOCK_log is inited by init_pthread_objects() */ + pthread_mutex_t LOCK_log; + char *name; + char log_file_name[FN_REFLEN]; + char time_buff[20], db[NAME_LEN + 1]; + bool write_error, inited; + IO_CACHE log_file; + enum_log_type log_type; + volatile enum_log_state log_state; + enum cache_type io_cache_type; + friend class Log_event; +}; + +class MYSQL_QUERY_LOG: public MYSQL_LOG +{ +public: + MYSQL_QUERY_LOG() : last_time(0) {} + void reopen_file(); + bool write(time_t event_time, const char *user_host, + uint user_host_len, int thread_id, + const char *command_type, uint command_type_len, + const char *sql_text, uint sql_text_len); + bool write(THD *thd, time_t current_time, time_t query_start_arg, + const char *user_host, uint user_host_len, + longlong query_time, longlong lock_time, bool is_command, + const char *sql_text, uint sql_text_len); + bool open_slow_log(const char *log_name) + { + char buf[FN_REFLEN]; + return open(generate_name(log_name, "-slow.log", 0, buf), LOG_NORMAL, 0, + WRITE_CACHE); + } + bool open_query_log(const char *log_name) + { + char buf[FN_REFLEN]; + return open(generate_name(log_name, ".log", 0, buf), LOG_NORMAL, 0, + WRITE_CACHE); + } +private: + time_t last_time; +}; + +class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG { private: /* LOCK_log and LOCK_index are inited by init_pthread_objects() */ - pthread_mutex_t LOCK_log, LOCK_index; + pthread_mutex_t LOCK_index; pthread_mutex_t LOCK_prep_xids; pthread_cond_t COND_prep_xids; pthread_cond_t update_cond; ulonglong bytes_written; - time_t last_time,query_start; - IO_CACHE log_file; IO_CACHE index_file; - char *name; - char time_buff[20],db[NAME_LEN+1]; - char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; + char index_file_name[FN_REFLEN]; /* The max size before rotation (usable only if log_type == LOG_BIN: binary logs and relay logs). @@ -186,13 +238,10 @@ class MYSQL_LOG: public TC_LOG */ ulong max_size; ulong prepared_xids; /* for tc log - number of xids to remember */ - volatile enum_log_type log_type; - enum cache_type io_cache_type; // current file sequence number for load data infile binary logging uint file_id; uint open_count; // For replication int readers_count; - bool write_error, inited; bool need_start_event; /* no_auto_events means we don't want any of these automatic events : @@ -202,13 +251,21 @@ class MYSQL_LOG: public TC_LOG In 5.0 it's 0 for relay logs too! */ bool no_auto_events; - friend class Log_event; ulonglong m_table_map_version; int write_to_file(IO_CACHE *cache); + /* + This is used to start writing to a new log file. The difference from + new_file() is locking. new_file_without_locking() does not acquire + LOCK_log. + */ + void new_file_without_locking(); + void new_file_impl(bool need_lock); public: + MYSQL_LOG::generate_name; + MYSQL_LOG::is_open; /* These describe the log's format. This is used only for relay logs. _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's @@ -220,9 +277,9 @@ public: Format_description_log_event *description_event_for_exec, *description_event_for_queue; - MYSQL_LOG(); + MYSQL_BIN_LOG(); /* - note that there's no destructor ~MYSQL_LOG() ! + note that there's no destructor ~MYSQL_BIN_LOG() ! The reason is that we don't want it to be automatically called on exit() - but only during the correct shutdown process */ @@ -264,9 +321,7 @@ public: void signal_update(); void wait_for_update(THD* thd, bool master_or_slave); void set_need_start_event() { need_start_event = 1; } - void init(enum_log_type log_type_arg, - enum cache_type io_cache_type_arg, - bool no_auto_events_arg, ulong max_size); + void init(bool no_auto_events_arg, ulong max_size); void init_pthread_objects(); void cleanup(); bool open(const char *log_name, @@ -275,35 +330,10 @@ public: enum cache_type io_cache_type_arg, bool no_auto_events_arg, ulong max_size, bool null_created); - const char *generate_name(const char *log_name, const char *suffix, - bool strip_ext, char *buff); - /* simplified open_xxx wrappers for the gigantic open above */ - bool open_query_log(const char *log_name) - { - char buf[FN_REFLEN]; - return open(generate_name(log_name, ".log", 0, buf), - LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0); - } - bool open_slow_log(const char *log_name) - { - char buf[FN_REFLEN]; - return open(generate_name(log_name, "-slow.log", 0, buf), - LOG_NORMAL, 0, WRITE_CACHE, 0, 0, 0); - } bool open_index_file(const char *index_file_name_arg, const char *log_name); - void new_file(bool need_lock); - /* log a command to the old-fashioned general log */ - bool write(time_t event_time, const char *user_host, - uint user_host_len, int thread_id, - const char *command_type, uint command_type_len, - const char *sql_text, uint sql_text_len); - - /* log a query to the old-fashioned slow query log */ - bool write(THD *thd, time_t current_time, time_t query_start_arg, - const char *user_host, uint user_host_len, - longlong query_time, longlong lock_time, bool is_command, - const char *sql_text, uint sql_text_len); + /* Use this to start writing a new log file */ + void new_file(); bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); @@ -319,7 +349,6 @@ public: bool appendv(const char* buf,uint len,...); bool append(Log_event* ev); - int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); int update_log_index(LOG_INFO* linfo, bool need_update_threads); @@ -339,7 +368,6 @@ public: int find_next_log(LOG_INFO* linfo, bool need_mutex); int get_current_log(LOG_INFO* linfo); uint next_file_id(); - inline bool is_open() { return log_type != LOG_CLOSED; } inline char* get_index_fname() { return index_file_name;} inline char* get_log_fname() { return log_file_name; } inline char* get_name() { return name; } @@ -416,7 +444,8 @@ public: class Log_to_file_event_handler: public Log_event_handler { - MYSQL_LOG mysql_log, mysql_slow_log; + MYSQL_QUERY_LOG mysql_log; + MYSQL_QUERY_LOG mysql_slow_log; bool is_initialized; public: Log_to_file_event_handler(): is_initialized(FALSE) diff --git a/sql/log_event.h b/sql/log_event.h index 36933f4a7dd..63588ed9b33 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -470,7 +470,7 @@ enum Int_event_type #ifndef MYSQL_CLIENT class String; -class MYSQL_LOG; +class MYSQL_BIN_LOG; class THD; #endif diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 83cbb383aef..637e4c665ad 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1542,7 +1542,7 @@ extern uint opt_large_page_size; extern char *opt_logname, *opt_slow_logname; extern const char *log_output_str; -extern MYSQL_LOG mysql_bin_log; +extern MYSQL_BIN_LOG mysql_bin_log; extern LOGGER logger; extern TABLE_LIST general_log, slow_log; extern FILE *bootstrap_file; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4313dd70b09..9d679699967 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2640,10 +2640,10 @@ static int init_common_variables(const char *conf_file_name, int argc, global_system_variables.time_zone= my_tz_SYSTEM; /* - Init mutexes for the global MYSQL_LOG objects. + Init mutexes for the global MYSQL_BIN_LOG objects. As safe_mutex depends on what MY_INIT() does, we can't init the mutexes of - global MYSQL_LOGs in their constructors, because then they would be inited - before MY_INIT(). So we do it here. + global MYSQL_BIN_LOGs in their constructors, because then they would be + inited before MY_INIT(). So we do it here. */ mysql_bin_log.init_pthread_objects(); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7fa47b0b005..4547b9e8100 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -107,7 +107,7 @@ public: uint8 min_flag, uint8 max_flag, uint8 maybe_flag); SEL_ARG(enum Type type_arg) :elements(1),use_count(1),left(0),next_key_part(0),color(BLACK), - type(type_arg) + type(type_arg),min_flag(0) {} inline bool is_same(SEL_ARG *arg) { diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index ce3f5c5f108..e87a478b8d6 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -651,7 +651,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, } else { - store_val_in_field(part->field, args[between && max_fl ? 2 : 1]); + store_val_in_field(part->field, args[between && max_fl ? 2 : 1], + CHECK_FIELD_IGNORE); if (part->null_bit) *key_ptr++= (byte) test(part->field->is_null()); part->field->get_key_image((char*) key_ptr, part->length, Field::itRAW); @@ -706,6 +707,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, field BETWEEN const1 AND const2 3. all references to the columns from the same table as column field occur only in conjucts mentioned above. + 4. each of k first components the index is not partial, i.e. is not + defined on a fixed length proper prefix of the field. If such an index exists the function through the ref parameter returns the key value to find max/min for the field using the index, @@ -715,8 +718,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, of the whole search key) NOTE - This function may set table->key_read to 1, which must be reset after - index is used! (This can only happen when function returns 1) + This function may set table->key_read to 1, which must be reset after + index is used! (This can only happen when function returns 1) RETURN 0 Index can not be used to optimize MIN(field)/MAX(field) @@ -750,6 +753,12 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) return 0; + /* Check whether the index component is partial */ + Field *part_field= table->field[part->fieldnr-1]; + if ((part_field->flags & BLOB_FLAG) || + part->length < part_field->key_length()) + break; + if (field->eq(part->field)) { ref->key= idx; diff --git a/sql/procedure.h b/sql/procedure.h index aceadd10883..5c8a3387eec 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -68,11 +68,11 @@ public: longlong val_int() { return (longlong) value; } String *val_str(String *s) { - s->set(value,decimals,default_charset()); + s->set_real(value,decimals,default_charset()); return s; } my_decimal *val_decimal(my_decimal *); - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} }; class Item_proc_int :public Item_proc @@ -91,7 +91,7 @@ public: longlong val_int() { return value; } String *val_str(String *s) { s->set(value, default_charset()); return s; } my_decimal *val_decimal(my_decimal *); - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} }; @@ -102,12 +102,12 @@ public: { this->max_length=length; } enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } - void set(double nr) { str_value.set(nr, 2, default_charset()); } + void set(double nr) { str_value.set_real(nr, 2, default_charset()); } void set(longlong nr) { str_value.set(nr, default_charset()); } void set(const char *str, uint length, CHARSET_INFO *cs) { str_value.copy(str,length,cs); } double val_real() - { + { int err_not_used; char *end_not_used; CHARSET_INFO *cs= str_value.charset(); diff --git a/sql/protocol.cc b/sql/protocol.cc index bb0891cdbbe..2edc11b6b3e 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -897,7 +897,7 @@ bool Protocol_simple::store(float from, uint32 decimals, String *buffer) field_types[field_pos] == MYSQL_TYPE_FLOAT); field_pos++; #endif - buffer->set((double) from, decimals, thd->charset()); + buffer->set_real((double) from, decimals, thd->charset()); return net_store_data((char*) buffer->ptr(), buffer->length()); } @@ -909,7 +909,7 @@ bool Protocol_simple::store(double from, uint32 decimals, String *buffer) field_types[field_pos] == MYSQL_TYPE_DOUBLE); field_pos++; #endif - buffer->set(from, decimals, thd->charset()); + buffer->set_real(from, decimals, thd->charset()); return net_store_data((char*) buffer->ptr(), buffer->length()); } diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc index 265f5f61213..ec0add4165f 100644 --- a/sql/rpl_injector.cc +++ b/sql/rpl_injector.cc @@ -25,7 +25,7 @@ /* inline since it's called below */ inline -injector::transaction::transaction(MYSQL_LOG *log, THD *thd) +injector::transaction::transaction(MYSQL_BIN_LOG *log, THD *thd) : m_state(START_STATE), m_thd(thd) { /* diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h index 14d5cca9b6c..3b0857e0833 100644 --- a/sql/rpl_injector.h +++ b/sql/rpl_injector.h @@ -26,7 +26,7 @@ /* Forward declarations */ class handler; -class MYSQL_LOG; +class MYSQL_BIN_LOG; class st_table; typedef st_table TABLE; @@ -219,7 +219,7 @@ public: private: /* Only the injector may construct these object */ - transaction(MYSQL_LOG *, THD *); + transaction(MYSQL_BIN_LOG *, THD *); void swap(transaction& o) { /* std::swap(m_start_pos, o.m_start_pos); */ diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 99606353080..392f12c2a71 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -69,7 +69,7 @@ typedef struct st_relay_log_info Protected with internal locks. Must get data_lock when resetting the logs. */ - MYSQL_LOG relay_log; + MYSQL_BIN_LOG relay_log; LOG_INFO linfo; IO_CACHE cache_buf,*cur_log; diff --git a/sql/slave.cc b/sql/slave.cc index 5ef91b1f0d4..f143faf427c 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -4658,7 +4658,7 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) When the relay log is created when the I/O thread starts, easy: the master will send the description event and we will queue it. But if the relay log is created by new_file(): then the solution is: - MYSQL_LOG::open() will write the buffered description event. + MYSQL_BIN_LOG::open() will write the buffered description event. */ if ((ev=Log_event::read_log_event(cur_log,0, rli->relay_log.description_event_for_exec))) @@ -4920,7 +4920,8 @@ err: Rotate a relay log (this is used only by FLUSH LOGS; the automatic rotation because of size is simpler because when we do it we already have all relevant locks; here we don't, so this function is mainly taking locks). - Returns nothing as we cannot catch any error (MYSQL_LOG::new_file() is void). + Returns nothing as we cannot catch any error (MYSQL_BIN_LOG::new_file() + is void). */ void rotate_relay_log(MASTER_INFO* mi) @@ -4942,7 +4943,7 @@ void rotate_relay_log(MASTER_INFO* mi) } /* If the relay log is closed, new_file() will do nothing. */ - rli->relay_log.new_file(1); + rli->relay_log.new_file(); /* We harvest now, because otherwise BIN_LOG_HEADER_SIZE will not immediately diff --git a/sql/slave.h b/sql/slave.h index 0b77d7f7c4f..053358dc686 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -73,7 +73,7 @@ run_lock protects all information about the run state: slave_running, and the existence of the I/O thread (to stop/start it, you need this mutex). data_lock protects some moving members of the struct: counters (log name, - position) and relay log (MYSQL_LOG object). + position) and relay log (MYSQL_BIN_LOG object). In RELAY_LOG_INFO: run_lock, data_lock see MASTER_INFO @@ -81,7 +81,7 @@ Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you must acquire LOCK_active_mi first. - In MYSQL_LOG: LOCK_log, LOCK_index of the binlog and the relay log + In MYSQL_BIN_LOG: LOCK_log, LOCK_index of the binlog and the relay log LOCK_log: when you write to it. LOCK_index: when you create/delete a binlog (so that you have to update the .index file). */ diff --git a/sql/sp.cc b/sql/sp.cc index 653c04ee11a..93e21170156 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -555,17 +555,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) table->field[MYSQL_PROC_FIELD_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); table->field[MYSQL_PROC_FIELD_TYPE]-> - store((longlong)type); + store((longlong)type, TRUE); table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> store(sp->m_name.str, sp->m_name.length, system_charset_info); if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS) table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)sp->m_chistics->daccess); + store((longlong)sp->m_chistics->daccess, TRUE); table->field[MYSQL_PROC_FIELD_DETERMINISTIC]-> - store((longlong)(sp->m_chistics->detistic ? 1 : 2)); + store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE); if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)sp->m_chistics->suid); + store((longlong)sp->m_chistics->suid, TRUE); table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> store(sp->m_params.str, sp->m_params.length, system_charset_info); if (sp->m_type == TYPE_ENUM_FUNCTION) @@ -582,7 +582,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); table->field[MYSQL_PROC_FIELD_SQL_MODE]-> - store((longlong)thd->variables.sql_mode); + store((longlong)thd->variables.sql_mode, TRUE); if (sp->m_chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]-> store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, @@ -686,10 +686,10 @@ db_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); if (chistics->suid != SP_IS_DEFAULT_SUID) table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)chistics->suid); + store((longlong)chistics->suid, TRUE); if (chistics->daccess != SP_DEFAULT_ACCESS) table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)chistics->daccess); + store((longlong)chistics->daccess, TRUE); if (chistics->comment.str) table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str, chistics->comment.length, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4c2dfac6b8b..8066c41fd10 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2030,7 +2030,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); if (table->s->fields >= 36 && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn); + table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; } if (old_row_exists) diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index f372c55c13e..9f1a0561138 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -1024,7 +1024,7 @@ String *field_decimal::avg(String *s, ha_rows rows) { if (!(rows - nulls)) { - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); return s; } my_decimal num, avg_val, rounded_avg; @@ -1045,7 +1045,7 @@ String *field_decimal::std(String *s, ha_rows rows) { if (!(rows - nulls)) { - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); return s; } my_decimal num, tmp, sum2, sum2d; @@ -1058,7 +1058,7 @@ String *field_decimal::std(String *s, ha_rows rows) my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp); my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr); - s->set(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), + s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), min(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset); return s; @@ -1092,7 +1092,7 @@ int collect_real(double *element, element_count count __attribute__((unused)), else info->found = 1; info->str->append('\''); - s.set(*element, info->item->decimals, current_thd->charset()); + s.set_real(*element, info->item->decimals, current_thd->charset()); info->str->append(s); info->str->append('\''); return 0; diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index 9e5926fd9b1..0e7bd1b722a 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -128,9 +128,9 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)), + s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } @@ -190,34 +190,34 @@ public: void add(); void get_opt_type(String*, ha_rows); - String *get_min_arg(String *s) - { - s->set(min_arg, item->decimals,my_thd_charset); - return s; + String *get_min_arg(String *s) + { + s->set_real(min_arg, item->decimals, my_thd_charset); + return s; } - String *get_max_arg(String *s) - { - s->set(max_arg, item->decimals,my_thd_charset); - return s; + String *get_max_arg(String *s) + { + s->set_real(max_arg, item->decimals, my_thd_charset); + return s; } String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset); + s->set_real(((double)sum / (double) (rows - nulls)), item->decimals,my_thd_charset); return s; } String *std(String *s, ha_rows rows) { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals,my_thd_charset); } return s; } @@ -249,21 +249,21 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) sum / (double) (rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } String *std(String *s, ha_rows rows) { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); } return s; } @@ -293,9 +293,9 @@ public: String *avg(String *s, ha_rows rows) { if (!(rows - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else - s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)), + s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)), DEC_IN_AVG,my_thd_charset); return s; } @@ -303,13 +303,13 @@ public: { double tmp = ulonglong2double(rows); if (!(tmp - nulls)) - s->set((double) 0.0, 1,my_thd_charset); + s->set_real((double) 0.0, 1,my_thd_charset); else { double tmp2 = ((ulonglong2double(sum_sqr) - ulonglong2double(sum * sum) / (tmp - nulls)) / (tmp - nulls)); - s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); + s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset); } return s; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9b7d46119b7..1b448a3ba18 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5084,10 +5084,6 @@ static bool setup_natural_join_row_types(THD *thd, if (from_clause->elements == 0) return FALSE; /* We come here in the case of UNIONs. */ - /* For stored procedures do not redo work if already done. */ - if (!context->select_lex->first_execution) - return FALSE; - List_iterator_fast table_ref_it(*from_clause); TABLE_LIST *table_ref; /* Current table reference. */ /* Table reference to the left of the current. */ @@ -5100,14 +5096,18 @@ static bool setup_natural_join_row_types(THD *thd, { table_ref= left_neighbor; left_neighbor= table_ref_it++; - if (store_top_level_join_columns(thd, table_ref, - left_neighbor, right_neighbor)) - return TRUE; - if (left_neighbor) + /* For stored procedures do not redo work if already done. */ + if (context->select_lex->first_execution) { - TABLE_LIST *first_leaf_on_the_right; - first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); - left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + if (store_top_level_join_columns(thd, table_ref, + left_neighbor, right_neighbor)) + return TRUE; + if (left_neighbor) + { + TABLE_LIST *first_leaf_on_the_right; + first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution(); + left_neighbor->next_name_resolution_table= first_leaf_on_the_right; + } } right_neighbor= table_ref; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0ede042da17..0a9e6472d9f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2121,7 +2121,9 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->enable_slow_log= enable_slow_log; backup->last_insert_id= last_insert_id; backup->next_insert_id= next_insert_id; + backup->current_insert_id= current_insert_id; backup->insert_id_used= insert_id_used; + backup->last_insert_id_used= last_insert_id_used; backup->clear_next_insert_id= clear_next_insert_id; backup->limit_found_rows= limit_found_rows; backup->examined_row_count= examined_row_count; @@ -2171,7 +2173,9 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) enable_slow_log= backup->enable_slow_log; last_insert_id= backup->last_insert_id; next_insert_id= backup->next_insert_id; + current_insert_id= backup->current_insert_id; insert_id_used= backup->insert_id_used; + last_insert_id_used= backup->last_insert_id_used; clear_next_insert_id= backup->clear_next_insert_id; limit_found_rows= backup->limit_found_rows; sent_row_count= backup->sent_row_count; diff --git a/sql/sql_class.h b/sql/sql_class.h index 450a8d041c9..723dad715bd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -763,12 +763,13 @@ class Sub_statement_state { public: ulonglong options; - ulonglong last_insert_id, next_insert_id; + ulonglong last_insert_id, next_insert_id, current_insert_id; ulonglong limit_found_rows; ha_rows cuted_fields, sent_row_count, examined_row_count; ulong client_capabilities; uint in_sub_stmt; bool enable_slow_log, insert_id_used, clear_next_insert_id; + bool last_insert_id_used; my_bool no_send_ok; SAVEPOINT *savepoints; }; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 0dae2b8f37b..f1f97400283 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2009,6 +2009,14 @@ bool delayed_insert::handle_inserts(void) if (!using_bin_log) table->file->extra(HA_EXTRA_WRITE_CACHE); pthread_mutex_lock(&mutex); + + /* Reset auto-increment cacheing */ + if (thd.clear_next_insert_id) + { + thd.next_insert_id= 0; + thd.clear_next_insert_id= 0; + } + while ((row=rows.get())) { stacked_inserts--; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 77f79ad61f4..15a94041d24 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1120,6 +1120,8 @@ typedef struct st_lex : public Query_tables_list case SQLCOM_UPDATE_MULTI: case SQLCOM_INSERT: case SQLCOM_INSERT_SELECT: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: case SQLCOM_LOAD: return TRUE; default: diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 14847a9906d..00aacd7b67b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5201,7 +5201,14 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) if (all_tables->security_ctx) thd->security_ctx= all_tables->security_ctx; - if (check_access(thd, privilege, all_tables->db, + const char *db_name; + if ((all_tables->view || all_tables->field_translation) && + !all_tables->schema_table) + db_name= all_tables->view_db.str; + else + db_name= all_tables->db; + + if (check_access(thd, privilege, db_name, &all_tables->grant.privilege, 0, 0, test(all_tables->schema_table))) goto deny; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2cd14c3f5e6..e9e2a4ed1e0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4823,7 +4823,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, */ bool -store_val_in_field(Field *field,Item *item) +store_val_in_field(Field *field, Item *item, enum_check_fields check_flag) { bool error; TABLE *table= field->table; @@ -4838,7 +4838,7 @@ store_val_in_field(Field *field,Item *item) with select_insert, which make count_cuted_fields= 1 */ enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; - thd->count_cuted_fields= CHECK_FIELD_WARN; + thd->count_cuted_fields= check_flag; error= item->save_in_field(field, 1); thd->count_cuted_fields= old_count_cuted_fields; dbug_tmp_restore_column_map(table->write_set, old_map); @@ -11031,7 +11031,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) field->real_type() != MYSQL_TYPE_VARCHAR && (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) { - return !store_val_in_field(field,right_item); + return !store_val_in_field(field, right_item, CHECK_FIELD_WARN); } } } diff --git a/sql/sql_select.h b/sql/sql_select.h index 6292977c209..2f0722db635 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -406,7 +406,7 @@ extern const char *join_type_str[]; void TEST_join(JOIN *join); /* Extern functions in sql_select.cc */ -bool store_val_in_field(Field *field,Item *val); +bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, ORDER *group, bool distinct, bool save_sum_fields, ulonglong select_options, ha_rows rows_limit, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 45137f5c617..12c98d8983b 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3423,7 +3423,7 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, show_table->field[key_part->fieldnr-1]->key_length())) { table->field[10]->store((longlong) key_part->length / - key_part->field->charset()->mbmaxlen); + key_part->field->charset()->mbmaxlen, TRUE); table->field[10]->set_notnull(); } uint flags= key_part->field ? key_part->field->flags : 0; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 19ee9f259dc..f86c05d2f4f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -96,29 +96,19 @@ bool String::realloc(uint32 alloc_length) return FALSE; } -bool String::set(longlong num, CHARSET_INFO *cs) +bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs) { uint l=20*cs->mbmaxlen+1; + int base= unsigned_flag ? 10 : -10; if (alloc(l)) return TRUE; - str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,-10,num); + str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,base,num); str_charset=cs; return FALSE; } -bool String::set(ulonglong num, CHARSET_INFO *cs) -{ - uint l=20*cs->mbmaxlen+1; - - if (alloc(l)) - return TRUE; - str_length=(uint32) (cs->cset->longlong10_to_str)(cs,Ptr,l,10,num); - str_charset=cs; - return FALSE; -} - -bool String::set(double num,uint decimals, CHARSET_INFO *cs) +bool String::set_real(double num,uint decimals, CHARSET_INFO *cs) { char buff[331]; uint dummy_errors; diff --git a/sql/sql_string.h b/sql/sql_string.h index ddae6368228..b1d417be2c2 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -139,9 +139,12 @@ public: } str_charset=cs; } - bool set(longlong num, CHARSET_INFO *cs); - bool set(ulonglong num, CHARSET_INFO *cs); - bool set(double num,uint decimals, CHARSET_INFO *cs); + bool set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs); + bool set(longlong num, CHARSET_INFO *cs) + { return set_int(num, false, cs); } + bool set(ulonglong num, CHARSET_INFO *cs) + { return set_int((longlong)num, true, cs); } + bool set_real(double num,uint decimals, CHARSET_INFO *cs); /* PMG 2004.11.12 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d379af3b3eb..bf0535567b4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5009,6 +5009,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, old_db_type= table->s->db_type; if (!create_info->db_type) { +#ifdef WITH_PARTITION_STORAGE_ENGINE if (table->part_info && create_info->used_fields & HA_CREATE_USED_ENGINE) { @@ -5022,6 +5023,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->db_type= table->part_info->default_engine_type; } else +#endif create_info->db_type= old_db_type; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index afcfed5be3f..32fdcfe45bd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -10635,6 +10635,8 @@ union_list: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } + /* This counter shouldn't be incremented for UNION parts */ + Lex->nest_level--; if (mysql_new_select(lex, 0)) YYABORT; mysql_init_select(lex); diff --git a/sql/structs.h b/sql/structs.h index 78f00f72df1..506b37eb31a 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -54,7 +54,13 @@ typedef struct st_key_part_info { /* Info about a key part */ Field *field; uint offset; /* offset in record (from 0) */ uint null_offset; /* Offset to null_bit in record */ - uint16 length; /* Length of key_part */ + uint16 length; /* Length of keypart value in bytes */ + /* + Number of bytes required to store the keypart value. This may be + different from the "length" field as it also counts + - possible NULL-flag byte (see HA_KEY_NULL_LENGTH) + - possible HA_KEY_BLOB_LENGTH bytes needed to store actual value length. + */ uint16 store_length; uint16 key_type; uint16 fieldnr; /* Fieldnum in UNIREG */ diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index e6c9c5dccd7..6c0796e3bdc 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -1473,7 +1473,7 @@ int ha_archive::end_bulk_insert() int ha_archive::delete_all_rows() { DBUG_ENTER("ha_archive::delete_all_rows"); - DBUG_RETURN(0); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); } /* diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index e5ddba7540d..6bd8447720c 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -61,7 +61,7 @@ TODO: /* The file extension */ #define CSV_EXT ".CSV" // The data file -#define CSN_EXT ".CSN" // Files used during repair +#define CSN_EXT ".CSN" // Files used during repair and update #define CSM_EXT ".CSM" // Meta file @@ -77,6 +77,53 @@ static int tina_init= 0; static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); static int tina_init_func(); +off_t Transparent_file::read_next() +{ + off_t bytes_read; + + /* + No need to seek here, as the file managed by Transparent_file class + always points to upper_bound byte + */ + if ((bytes_read= my_read(filedes, buff, buff_size, MYF(0))) == MY_FILE_ERROR) + return -1; + + /* end of file */ + if (!bytes_read) + return -1; + + lower_bound= upper_bound; + upper_bound+= bytes_read; + + return lower_bound; +} + + +char Transparent_file::get_value(off_t offset) +{ + off_t bytes_read; + + /* check boundaries */ + if ((lower_bound <= offset) && (offset < upper_bound)) + return buff[offset - lower_bound]; + else + { + VOID(my_seek(filedes, offset, MY_SEEK_SET, MYF(0))); + /* read appropriate portion of the file */ + if ((bytes_read= my_read(filedes, buff, buff_size, + MYF(0))) == MY_FILE_ERROR) + return 0; + + lower_bound= offset; + upper_bound= lower_bound + bytes_read; + + /* end of file */ + if (upper_bound == offset) + return 0; + + return buff[0]; + } +} handlerton tina_hton; /***************************************************************************** @@ -92,7 +139,7 @@ int sort_set (tina_set *a, tina_set *b) We assume that intervals do not intersect. So, it is enought to compare any two points. Here we take start of intervals for comparison. */ - return ( a->begin > b->begin ? -1 : ( a->begin < b->begin ? 1 : 0 ) ); + return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) ); } static byte* tina_get_key(TINA_SHARE *share,uint *length, @@ -102,55 +149,6 @@ static byte* tina_get_key(TINA_SHARE *share,uint *length, return (byte*) share->table_name; } -/* - Reloads the mmap file. -*/ -int get_mmap(TINA_SHARE *share, int write) -{ - DBUG_ENTER("ha_tina::get_mmap"); -#ifdef __NETWARE__ - my_message(errno, "Sorry, no mmap() on Netware", 0); - DBUG_ASSERT(0); - DBUG_RETURN(1); -#else - if (share->mapped_file && my_munmap(share->mapped_file, - share->file_stat.st_size)) - DBUG_RETURN(1); - - if (my_fstat(share->data_file, &share->file_stat, MYF(MY_WME)) == -1) - DBUG_RETURN(1); - - if (share->file_stat.st_size) - { - if (write) - share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size, - PROT_READ|PROT_WRITE, MAP_SHARED, - share->data_file, 0); - else - share->mapped_file= (byte *)my_mmap(NULL, share->file_stat.st_size, - PROT_READ, MAP_PRIVATE, - share->data_file, 0); - if ((share->mapped_file == MAP_FAILED)) - { - /* - Bad idea you think? See the problem is that nothing actually checks - the return value of ::rnd_init(), so tossing an error is about - it for us. - Never going to happen right? :) - */ - my_message(errno, "Woops, blew up opening a mapped file", 0); - DBUG_ASSERT(0); - DBUG_RETURN(1); - } - } - else - share->mapped_file= NULL; - - DBUG_RETURN(0); -#endif /* __NETWARE__ */ -} - - static int tina_init_func() { if (!tina_init) @@ -191,6 +189,7 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) { TINA_SHARE *share; char meta_file_name[FN_REFLEN]; + MY_STAT file_stat; /* Stat information for the data file */ char *tmp_name; uint length; @@ -223,6 +222,8 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) share->table_name= tmp_name; share->crashed= FALSE; share->rows_recorded= 0; + share->update_file_opened= FALSE; + share->tina_write_opened= FALSE; strmov(share->table_name, table_name); fn_format(share->data_file_name, table_name, "", CSV_EXT, MY_REPLACE_EXT|MY_UNPACK_FILENAME); @@ -244,33 +245,21 @@ static TINA_SHARE *get_share(const char *table_name, TABLE *table) share->crashed= TRUE; /* - After we read, we set the file to dirty. When we close, we will do the - opposite. If the meta file will not open we assume it is crashed and + If the meta file will not open we assume it is crashed and mark it as such. */ if (read_meta_file(share->meta_file, &share->rows_recorded)) share->crashed= TRUE; - else - (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); - if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, - MYF(0))) == -1) + if (my_stat(share->data_file_name, &file_stat, MYF(MY_WME)) == NULL) goto error2; - - share->mapped_file= NULL; // We don't know the state as we just allocated it - if (get_mmap(share, 0) > 0) - goto error3; - - /* init file length value used by readers */ - share->saved_data_file_length= share->file_stat.st_size; + share->saved_data_file_length= file_stat.st_size; } share->use_count++; pthread_mutex_unlock(&tina_mutex); return share; -error3: - my_close(share->data_file,MYF(0)); error2: thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); @@ -398,6 +387,30 @@ bool ha_tina::check_and_repair(THD *thd) } +int ha_tina::init_tina_writer() +{ + DBUG_ENTER("ha_tina::init_tina_writer"); + + /* + Mark the file as crashed. We will set the flag back when we close + the file. In the case of the crash it will remain marked crashed, + which enforce recovery. + */ + (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); + + if ((share->tina_write_filedes= + my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(0))) == -1) + { + DBUG_PRINT("info", ("Could not open tina file writes")); + share->crashed= TRUE; + DBUG_RETURN(1); + } + share->tina_write_opened= TRUE; + + DBUG_RETURN(0); +} + + bool ha_tina::is_crashed() const { DBUG_ENTER("ha_tina::is_crashed"); @@ -418,10 +431,13 @@ static int free_share(TINA_SHARE *share) share->crashed ? TRUE :FALSE); if (my_close(share->meta_file, MYF(0))) result_code= 1; - if (share->mapped_file) - my_munmap(share->mapped_file, share->file_stat.st_size); - share->mapped_file= NULL; - result_code= my_close(share->data_file,MYF(0)); + if (share->tina_write_opened) + { + if (my_close(share->tina_write_filedes, MYF(0))) + result_code= 1; + share->tina_write_opened= FALSE; + } + hash_delete(&tina_open_tables, (byte*) share); thr_lock_delete(&share->lock); pthread_mutex_destroy(&share->mutex); @@ -437,30 +453,41 @@ int tina_end(ha_panic_function type) return tina_done_func(); } -/* - Finds the end of a line. - Supports DOS, Unix, or Mac OS line endings. -*/ -byte * find_eoln(byte *data, off_t begin, off_t end, int *eoln_len) -{ - off_t dataend= begin; - *eoln_len= 0; - - for (off_t x= begin; x < end; x++) - if (data[x] == '\r' || data[x] == '\n') - (*eoln_len)++; - else if (!(*eoln_len)) - dataend++; - else - return data+dataend; - /* - if we only have one record in the file then our for loop will break - before we return. we should still have seen end of line markers and - so we just return the line here - */ - if (*eoln_len > 0) - return data+dataend; +/* + This function finds the end of a line and returns the length + of the line ending. + + We support three kinds of line endings: + '\r' -- Old Mac OS line ending + '\n' -- Traditional Unix and Mac OS X line ending + '\r''\n' -- DOS\Windows line ending +*/ + +off_t find_eoln_buff(Transparent_file *data_buff, off_t begin, + off_t end, int *eoln_len) +{ + *eoln_len= 0; + + for (off_t x= begin; x < end; x++) + { + /* Unix (includes Mac OS X) */ + if (data_buff->get_value(x) == '\n') + *eoln_len= 1; + else + if (data_buff->get_value(x) == '\r') // Mac or Dos + { + /* old Mac line ending */ + if (x + 1 == end || (data_buff->get_value(x + 1) != '\n')) + *eoln_len= 1; + else // DOS style ending + *eoln_len= 2; + } + + if (*eoln_len) // end of line was found + return x; + } + return 0; } @@ -478,12 +505,13 @@ ha_tina::ha_tina(TABLE_SHARE *table_arg) They are not probably completely right. */ current_position(0), next_position(0), local_saved_data_file_length(0), - chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), + file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0) { /* Set our original buffers from pre-allocated memory */ buffer.set((char*)byte_buffer, IO_SIZE, system_charset_info); chain= chain_buffer; + file_buff= new Transparent_file(); } @@ -609,20 +637,18 @@ int ha_tina::chain_append() */ int ha_tina::find_current_row(byte *buf) { - byte *mapped_ptr; - byte *end_ptr; + off_t end_offset, curr_offset= current_position; int eoln_len; my_bitmap_map *org_bitmap; DBUG_ENTER("ha_tina::find_current_row"); - mapped_ptr= (byte *)share->mapped_file + current_position; - /* We do not read further then local_saved_data_file_length in order not to conflict with undergoing concurrent insert. */ - if ((end_ptr= find_eoln(share->mapped_file, current_position, - local_saved_data_file_length, &eoln_len)) == 0) + if ((end_offset= + find_eoln_buff(file_buff, current_position, + local_saved_data_file_length, &eoln_len)) == 0) DBUG_RETURN(HA_ERR_END_OF_FILE); /* Avoid asserts in ::store() for columns that are not going to be updated */ @@ -631,36 +657,39 @@ int ha_tina::find_current_row(byte *buf) for (Field **field=table->field ; *field ; field++) { buffer.length(0); - if (*mapped_ptr == '"') - mapped_ptr++; // Increment past the first quote + if (file_buff->get_value(curr_offset) == '"') + curr_offset++; // Incrementpast the first quote else { dbug_tmp_restore_column_map(table->write_set, org_bitmap); DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); } - for(;mapped_ptr != end_ptr; mapped_ptr++) + for(;curr_offset != end_offset; curr_offset++) { // Need to convert line feeds! - if (*mapped_ptr == '"' && - (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) || - (mapped_ptr == end_ptr -1 ))) + if (file_buff->get_value(curr_offset) == '"' && + (((file_buff->get_value(curr_offset + 1) == ',') && + (file_buff->get_value(curr_offset + 2) == '"')) || + (curr_offset == end_offset -1 ))) { - mapped_ptr += 2; // Move past the , and the " + curr_offset+= 2; // Move past the , and the " break; } - if (*mapped_ptr == '\\' && mapped_ptr != (end_ptr - 1)) + if (file_buff->get_value(curr_offset) == '\\' && + curr_offset != (end_offset - 1)) { - mapped_ptr++; - if (*mapped_ptr == 'r') + curr_offset++; + if (file_buff->get_value(curr_offset) == 'r') buffer.append('\r'); - else if (*mapped_ptr == 'n' ) + else if (file_buff->get_value(curr_offset) == 'n' ) buffer.append('\n'); - else if ((*mapped_ptr == '\\') || (*mapped_ptr == '"')) - buffer.append(*mapped_ptr); + else if ((file_buff->get_value(curr_offset) == '\\') || + (file_buff->get_value(curr_offset) == '"')) + buffer.append(file_buff->get_value(curr_offset)); else /* This could only happed with an externally created file */ { buffer.append('\\'); - buffer.append(*mapped_ptr); + buffer.append(file_buff->get_value(curr_offset)); } } else // ordinary symbol @@ -669,18 +698,18 @@ int ha_tina::find_current_row(byte *buf) We are at final symbol and no last quote was found => we are working with a damaged file. */ - if (mapped_ptr == end_ptr -1) + if (curr_offset == end_offset - 1) { dbug_tmp_restore_column_map(table->write_set, org_bitmap); DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); } - buffer.append(*mapped_ptr); + buffer.append(file_buff->get_value(curr_offset)); } } if (bitmap_is_set(table->read_set, (*field)->field_index)) (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); } - next_position= (end_ptr - share->mapped_file)+eoln_len; + next_position= end_offset + eoln_len; /* Maybe use \N for null? */ memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */ dbug_tmp_restore_column_map(table->write_set, org_bitmap); @@ -779,7 +808,7 @@ void ha_tina::get_status() void ha_tina::update_status() { /* correct local_saved_data_file_length for writers */ - share->saved_data_file_length= share->file_stat.st_size; + share->saved_data_file_length= local_saved_data_file_length; } @@ -830,6 +859,9 @@ int ha_tina::open(const char *name, int mode, uint open_options) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); } + if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) + DBUG_RETURN(0); + /* Init locking. Pass handler object to the locking routines, so that they could save/update local_saved_data_file_length value @@ -848,12 +880,14 @@ int ha_tina::open(const char *name, int mode, uint open_options) /* Close a database file. We remove ourselves from the shared strucutre. - If it is empty we destroy it and free the mapped file. + If it is empty we destroy it. */ int ha_tina::close(void) { + int rc= 0; DBUG_ENTER("ha_tina::close"); - DBUG_RETURN(free_share(share)); + rc= my_close(data_file, MYF(0)); + DBUG_RETURN(free_share(share) || rc); } /* @@ -876,22 +910,17 @@ int ha_tina::write_row(byte * buf) size= encode_quote(buf); - if (my_write(share->data_file, (byte*)buffer.ptr(), size, + if (!share->tina_write_opened) + if (init_tina_writer()) + DBUG_RETURN(-1); + + /* use pwrite, as concurrent reader could have changed the position */ + if (my_write(share->tina_write_filedes, (byte*)buffer.ptr(), size, MYF(MY_WME | MY_NABP))) DBUG_RETURN(-1); - /* - Ok, this is means that we will be doing potentially bad things - during a bulk insert on some OS'es. What we need is a cleanup - call for ::write_row that would let us fix up everything after the bulk - insert. The archive handler does this with an extra mutx call, which - might be a solution for this. - */ - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); - /* update local copy of the max position to see our own changes */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length+= size; /* update shared info */ pthread_mutex_lock(&share->mutex); @@ -906,6 +935,23 @@ int ha_tina::write_row(byte * buf) } +int ha_tina::open_update_temp_file_if_needed() +{ + char updated_fname[FN_REFLEN]; + + if (!share->update_file_opened) + { + if ((update_temp_file= + my_create(fn_format(updated_fname, share->table_name, + "", CSN_EXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME), + 0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0) + return 1; + share->update_file_opened= TRUE; + } + return 0; +} + /* This is called for an update. Make sure you put in code to increment the auto increment, also @@ -929,16 +975,16 @@ int ha_tina::update_row(const byte * old_data, byte * new_data) if (chain_append()) DBUG_RETURN(-1); - if (my_write(share->data_file, (byte*)buffer.ptr(), size, + if (open_update_temp_file_if_needed()) + DBUG_RETURN(-1); + + if (my_write(update_temp_file, (byte*)buffer.ptr(), size, MYF(MY_WME | MY_NABP))) DBUG_RETURN(-1); /* UPDATE should never happen on the log tables */ DBUG_ASSERT(!share->is_log_table); - /* update local copy of the max position to see our own changes */ - local_saved_data_file_length= share->file_stat.st_size; - DBUG_RETURN(0); } @@ -1004,6 +1050,8 @@ int ha_tina::rnd_init(bool scan) { DBUG_ENTER("ha_tina::rnd_init"); + /* set buffer to the beginning of the file */ + file_buff->init_buff(data_file); if (share->crashed) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); @@ -1011,11 +1059,6 @@ int ha_tina::rnd_init(bool scan) stats.records= 0; records_is_known= 0; chain_ptr= chain; -#ifdef HAVE_MADVISE - if (scan) - (void) madvise(share->mapped_file, share->file_stat.st_size, - MADV_SEQUENTIAL); -#endif DBUG_RETURN(0); } @@ -1045,8 +1088,11 @@ int ha_tina::rnd_next(byte *buf) ha_statistic_increment(&SSV::ha_read_rnd_next_count); current_position= next_position; - if (!share->mapped_file) + + /* don't scan an empty file */ + if (!local_saved_data_file_length) DBUG_RETURN(HA_ERR_END_OF_FILE); + if ((rc= find_current_row(buf))) DBUG_RETURN(rc); @@ -1115,6 +1161,22 @@ int ha_tina::extra(enum ha_extra_function operation) DBUG_RETURN(0); } +/* + Set end_pos to the last valid byte of continuous area, closest + to the given "hole", stored in the buffer. "Valid" here means, + not listed in the chain of deleted records ("holes"). +*/ +bool ha_tina::get_write_pos(off_t *end_pos, tina_set *closest_hole) +{ + if (closest_hole == chain_ptr) /* no more chains */ + *end_pos= file_buff->end(); + else + *end_pos= min(file_buff->end(), + closest_hole->begin); + return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin); +} + + /* Called after each table scan. In particular after deletes, and updates. In the last case we employ chain of deleted @@ -1123,53 +1185,107 @@ int ha_tina::extra(enum ha_extra_function operation) */ int ha_tina::rnd_end() { + char updated_fname[FN_REFLEN]; + off_t file_buffer_start= 0; DBUG_ENTER("ha_tina::rnd_end"); records_is_known= 1; - /* First position will be truncate position, second will be increment */ if ((chain_ptr - chain) > 0) { - tina_set *ptr; - size_t length; + tina_set *ptr= chain; /* - Setting up writable map, this will contain all of the data after the - get_mmap call that we have added to the file. + Re-read the beginning of a file (as the buffer should point to the + end of file after the scan). */ - if (get_mmap(share, 1) > 0) - DBUG_RETURN(-1); - length= share->file_stat.st_size; + file_buff->init_buff(data_file); /* - The sort handles updates/deletes with random orders. - It also sorts so that we move the final blocks to the - beginning so that we move the smallest amount of data possible. + The sort is needed when there were updates/deletes with random orders. + It sorts so that we move the firts blocks to the beginning. */ qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set); - for (ptr= chain; ptr < chain_ptr; ptr++) + + off_t write_begin= 0, write_end; + + /* create the file to write updated table if it wasn't yet created */ + if (open_update_temp_file_if_needed()) + DBUG_RETURN(-1); + + /* write the file with updated info */ + while ((file_buffer_start != -1)) // while not end of file { - memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end, - length - (size_t)ptr->end); - length= length - (size_t)(ptr->end - ptr->begin); + bool in_hole= get_write_pos(&write_end, ptr); + + /* if there is something to write, write it */ + if ((write_end - write_begin) && + (my_write(update_temp_file, + (byte*)(file_buff->ptr() + + (write_begin - file_buff->start())), + write_end - write_begin, MYF_RW))) + goto error; + + if (in_hole) + { + /* skip hole */ + while (file_buff->end() <= ptr->end && file_buffer_start != -1) + file_buffer_start= file_buff->read_next(); + write_begin= ptr->end; + ptr++; + } + else + write_begin= write_end; + + if (write_end == file_buff->end()) + file_buffer_start= file_buff->read_next(); /* shift the buffer */ + } - /* Unmap the file before the new size is set */ - if (my_munmap(share->mapped_file, share->file_stat.st_size)) - DBUG_RETURN(-1); - /* We set it to null so that get_mmap() won't try to unmap it */ - share->mapped_file= NULL; - - /* Set the file to the new size */ - if (my_chsize(share->data_file, length, 0, MYF(MY_WME))) + if (my_sync(update_temp_file, MYF(MY_WME)) || + my_close(update_temp_file, MYF(0))) DBUG_RETURN(-1); - if (get_mmap(share, 0) > 0) + share->update_file_opened= FALSE; + + if (share->tina_write_opened) + { + if (my_close(share->tina_write_filedes, MYF(0))) + DBUG_RETURN(-1); + /* + Mark that the writer fd is closed, so that init_tina_writer() + will reopen it later. + */ + share->tina_write_opened= FALSE; + } + + /* + Close opened fildes's. Then move updated file in place + of the old datafile. + */ + if (my_close(data_file, MYF(0)) || + my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME), + share->data_file_name, MYF(0))) DBUG_RETURN(-1); + + /* Open the file again */ + if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)) + DBUG_RETURN(-1); + /* + The datafile is consistent at this point and the write filedes is + closed, so nothing worrying will happen to it in case of a crash. + Here we record this fact to the meta-file. + */ + (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE); } DBUG_RETURN(0); +error: + my_close(update_temp_file, MYF(0)); + share->update_file_opened= FALSE; + DBUG_RETURN(-1); } @@ -1198,10 +1314,11 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) File repair_file; int rc; ha_rows rows_repaired= 0; + off_t write_begin= 0, write_end; DBUG_ENTER("ha_tina::repair"); /* empty file */ - if (!share->mapped_file) + if (!share->saved_data_file_length) { share->rows_recorded= 0; goto end; @@ -1212,12 +1329,15 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + /* position buffer to the start of the file */ + file_buff->init_buff(data_file); + /* Local_saved_data_file_length is initialized during the lock phase. Sometimes this is not getting executed before ::repair (e.g. for the log tables). We set it manually here. */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length= share->saved_data_file_length; /* set current position to the beginning of the file */ current_position= next_position= 0; @@ -1232,11 +1352,10 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) if (rc == HA_ERR_END_OF_FILE) { - /* All rows were read ok until end of file, the file does not need repair. */ - /* - If rows_recorded != rows_repaired, we should update - rows_recorded value to the current amount of rows. + All rows were read ok until end of file, the file does not need repair. + If rows_recorded != rows_repaired, we should update rows_recorded value + to the current amount of rows. */ share->rows_recorded= rows_repaired; goto end; @@ -1252,36 +1371,45 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) 0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR); - if (my_write(repair_file, (byte*)share->mapped_file, current_position, - MYF(MY_NABP))) - DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); - my_close(repair_file, MYF(0)); + file_buff->init_buff(data_file); + + /* we just truncated the file up to the first bad row. update rows count. */ share->rows_recorded= rows_repaired; - if (my_munmap(share->mapped_file, share->file_stat.st_size)) - DBUG_RETURN(-1); - /* We set it to null so that get_mmap() won't try to unmap it */ - share->mapped_file= NULL; + /* write repaired file */ + while (1) + { + write_end= min(file_buff->end(), current_position); + if ((write_end - write_begin) && + (my_write(repair_file, (byte*)file_buff->ptr(), + write_end - write_begin, MYF_RW))) + DBUG_RETURN(-1); + + write_begin= write_end; + if (write_end== current_position) + break; + else + file_buff->read_next(); /* shift the buffer */ + } /* - Close the "to"-file before renaming - On Windows one cannot rename a file, which descriptor - is still open. EACCES will be returned when trying to delete - the "to"-file in my_rename() + Close the files and rename repaired file to the datafile. + We have to close the files, as on Windows one cannot rename + a file, which descriptor is still open. EACCES will be returned + when trying to delete the "to"-file in my_rename(). */ - my_close(share->data_file,MYF(0)); - - if (my_rename(repaired_fname, share->data_file_name, MYF(0))) + if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) || + my_rename(repaired_fname, share->data_file_name, MYF(0))) DBUG_RETURN(-1); /* Open the file again, it should now be repaired */ - if ((share->data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, - MYF(0))) == -1) + if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, + MYF(0))) == -1) DBUG_RETURN(-1); - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); + /* Set new file size. The file size will be updated by ::update_status() */ + local_saved_data_file_length= (size_t) current_position; end: share->crashed= FALSE; @@ -1300,17 +1428,12 @@ int ha_tina::delete_all_rows() if (!records_is_known) DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND); - /* Unmap the file before the new size is set */ - if (share->mapped_file && my_munmap(share->mapped_file, - share->file_stat.st_size)) - DBUG_RETURN(-1); - share->mapped_file= NULL; + if (!share->tina_write_opened) + if (init_tina_writer()) + DBUG_RETURN(-1); /* Truncate the file to zero size */ - rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME)); - - if (get_mmap(share, 0) > 0) - DBUG_RETURN(-1); + rc= my_chsize(share->tina_write_filedes, 0, 0, MYF(MY_WME)); stats.records=0; DBUG_RETURN(rc); @@ -1372,12 +1495,15 @@ int ha_tina::check(THD* thd, HA_CHECK_OPT* check_opt) if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); + /* position buffer to the start of the file */ + file_buff->init_buff(data_file); + /* Local_saved_data_file_length is initialized during the lock phase. Check does not use store_lock in certain cases. So, we set it manually here. */ - local_saved_data_file_length= share->file_stat.st_size; + local_saved_data_file_length= share->saved_data_file_length; /* set current position to the beginning of the file */ current_position= next_position= 0; /* Read the file row-by-row. If everything is ok, repair is not needed. */ diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index d3a8c5092b6..14ca848c855 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -29,15 +29,12 @@ typedef struct st_tina_share { char *table_name; char data_file_name[FN_REFLEN]; - byte *mapped_file; /* mapped region of file */ uint table_name_length, use_count; /* Below flag is needed to make log tables work with concurrent insert. For more details see comment to ha_tina::update_status. */ my_bool is_log_table; - MY_STAT file_stat; /* Stat information for the data file */ - File data_file; /* Current open data file */ /* Here we save the length of the file for readers. This is updated by inserts, updates and deletes. The var is initialized along with the @@ -46,7 +43,10 @@ typedef struct st_tina_share { off_t saved_data_file_length; pthread_mutex_t mutex; THR_LOCK lock; + bool update_file_opened; + bool tina_write_opened; File meta_file; /* Meta file we use */ + File tina_write_filedes; /* File handler for readers */ bool crashed; /* Meta file is crashed */ ha_rows rows_recorded; /* Number of rows in tables */ } TINA_SHARE; @@ -56,6 +56,49 @@ struct tina_set { off_t end; }; +class Transparent_file +{ + File filedes; + byte *buff; /* in-memory window to the file or mmaped area */ + /* current window sizes */ + off_t lower_bound; + off_t upper_bound; + uint buff_size; + + public: + + Transparent_file() : lower_bound(0), buff_size(IO_SIZE) + { buff= (byte *) my_malloc(buff_size*sizeof(byte), MYF(MY_WME)); } + + ~Transparent_file() + { my_free((gptr)buff, MYF(MY_ALLOW_ZERO_PTR)); } + + void init_buff(File filedes_arg) + { + filedes= filedes_arg; + /* read the beginning of the file */ + lower_bound= 0; + VOID(my_seek(filedes, 0, MY_SEEK_SET, MYF(0))); + if (filedes && buff) + upper_bound= my_read(filedes, buff, buff_size, MYF(0)); + } + + byte *ptr() + { return buff; } + + off_t start() + { return lower_bound; } + + off_t end() + { return upper_bound; } + + /* get a char from the given position in the file */ + char get_value (off_t offset); + /* shift a buffer windows to see the next part of the file */ + off_t read_next(); + +}; + class ha_tina: public handler { THR_LOCK_DATA lock; /* MySQL lock */ @@ -64,6 +107,9 @@ class ha_tina: public handler off_t next_position; /* Next position in the file scan */ off_t local_saved_data_file_length; /* save position for reads */ byte byte_buffer[IO_SIZE]; + Transparent_file *file_buff; + File data_file; /* File handler for readers */ + File update_temp_file; String buffer; /* The chain contains "holes" in the file, occured because of @@ -77,12 +123,19 @@ class ha_tina: public handler uint32 chain_size; bool records_is_known; +private: + bool get_write_pos(off_t *end_pos, tina_set *closest_hole); + int open_update_temp_file_if_needed(); + int init_tina_writer(); + public: ha_tina(TABLE_SHARE *table_arg); - ~ha_tina() + ~ha_tina() { if (chain_alloced) - my_free((gptr)chain,0); + my_free((gptr)chain, 0); + if (file_buff) + delete file_buff; } const char *table_type() const { return "CSV"; } const char *index_type(uint inx) { return "NONE"; } diff --git a/storage/ndb/include/mgmapi/mgmapi.h b/storage/ndb/include/mgmapi/mgmapi.h index 5a0ffcfe2c6..0785a5dba99 100644 --- a/storage/ndb/include/mgmapi/mgmapi.h +++ b/storage/ndb/include/mgmapi/mgmapi.h @@ -699,6 +699,28 @@ extern "C" { int ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, int abort); + /** + * Stops cluster nodes + * + * @param handle Management handle. + * @param no_of_nodes Number of database nodes to stop
+ * -1: All database and management nodes
+ * 0: All database nodes in cluster
+ * n: Stop the n node(s) specified in + * the array node_list + * @param node_list List of node IDs of database nodes to be stopped + * @param abort Don't perform graceful stop, + * but rather stop immediately + * @param disconnect Returns true if you need to disconnect to apply + * the stop command (e.g. stopping the mgm server + * that handle is connected to) + * + * @return Number of nodes stopped (-1 on error). + */ + int ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, + const int * node_list, int abort, int *disconnect); + + /** * Restart database nodes * @@ -738,6 +760,31 @@ extern "C" { const int * node_list, int initial, int nostart, int abort); + /** + * Restart nodes + * + * @param handle Management handle. + * @param no_of_nodes Number of database nodes to be restarted:
+ * 0: Restart all database nodes in the cluster
+ * n: Restart the n node(s) specified in the + * array node_list + * @param node_list List of node IDs of database nodes to be restarted + * @param initial Remove filesystem from restarting node(s) + * @param nostart Don't actually start node(s) but leave them + * waiting for start command + * @param abort Don't perform graceful restart, + * but rather restart immediately + * @param disconnect Returns true if mgmapi client must disconnect from + * server to apply the requested operation. (e.g. + * restart the management server) + * + * + * @return Number of nodes stopped (-1 on error). + */ + int ndb_mgm_restart3(NdbMgmHandle handle, int no_of_nodes, + const int * node_list, int initial, + int nostart, int abort, int *disconnect); + /** * Start database nodes * @@ -1018,6 +1065,16 @@ extern "C" { */ Uint32 ndb_mgm_get_mgmd_nodeid(NdbMgmHandle handle); + /** + * Get the version of the mgm server we're talking to. + * Designed to allow switching of protocol depending on version + * so that new clients can speak to old servers in a compat mode + */ + int ndb_mgm_get_version(NdbMgmHandle handle, + int *major, int *minor, int* build, + int len, char* str); + + /** * Config iterator */ diff --git a/storage/ndb/include/ndbapi/NdbBlob.hpp b/storage/ndb/include/ndbapi/NdbBlob.hpp index 089b70339de..e8944d684d3 100644 --- a/storage/ndb/include/ndbapi/NdbBlob.hpp +++ b/storage/ndb/include/ndbapi/NdbBlob.hpp @@ -79,19 +79,41 @@ class NdbEventOperationImpl; * Non-void NdbBlob methods return -1 on error and 0 on success. Output * parameters are used when necessary. * - * Operation types: - * - insertTuple must use setValue if blob column is non-nullable - * - readTuple with exclusive lock can also update existing value - * - updateTuple can overwrite with setValue or update existing value - * - writeTuple always overwrites and must use setValue if non-nullable + * Usage notes for different operation types: + * + * - insertTuple must use setValue if blob attribute is non-nullable + * + * - readTuple or scan readTuples with lock mode LM_CommittedRead is + * automatically upgraded to lock mode LM_Read if any blob attributes + * are accessed (to guarantee consistent view) + * + * - readTuple (with any lock mode) can only read blob value + * + * - updateTuple can either overwrite existing value with setValue or + * update it in active phase + * + * - writeTuple always overwrites blob value and must use setValue if + * blob attribute is non-nullable + * * - deleteTuple creates implicit non-accessible blob handles - * - scan with exclusive lock can also update existing value - * - scan "lock takeover" update op must do its own getBlobHandle + * + * - scan readTuples (any lock mode) can use its blob handles only + * to read blob value + * + * - scan readTuples with lock mode LM_Exclusive can update row and blob + * value using updateCurrentTuple, where the operation returned must + * create its own blob handles explicitly + * + * - scan readTuples with lock mode LM_Exclusive can delete row (and + * therefore blob values) using deleteCurrentTuple, which creates + * implicit non-accessible blob handles + * + * - the operation returned by lockCurrentTuple cannot update blob value * * Bugs / limitations: - * - lock mode upgrade should be handled automatically - * - lock mode vs allowed operation is not checked + * * - too many pending blob ops can blow up i/o buffers + * * - table and its blob part tables are not created atomically */ #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL @@ -214,6 +236,9 @@ public: /** * Return error object. The error may be blob specific or may be * copied from a failed implicit operation. + * + * The error code is copied back to the operation unless the operation + * already has a non-zero error code. */ const NdbError& getNdbError() const; /** @@ -326,6 +351,7 @@ private: bool isWriteOp(); bool isDeleteOp(); bool isScanOp(); + bool isReadOnlyOp(); bool isTakeOverOp(); // computations Uint32 getPartNumber(Uint64 pos); @@ -367,10 +393,10 @@ private: int atNextResult(); int atNextEvent(); // errors - void setErrorCode(int anErrorCode, bool invalidFlag = true); - void setErrorCode(NdbOperation* anOp, bool invalidFlag = true); - void setErrorCode(NdbTransaction* aCon, bool invalidFlag = true); - void setErrorCode(NdbEventOperationImpl* anOp, bool invalidFlag = true); + void setErrorCode(int anErrorCode, bool invalidFlag = false); + void setErrorCode(NdbOperation* anOp, bool invalidFlag = false); + void setErrorCode(NdbTransaction* aCon, bool invalidFlag = false); + void setErrorCode(NdbEventOperationImpl* anOp, bool invalidFlag = false); #ifdef VM_TRACE int getOperationType() const; friend class NdbOut& operator<<(NdbOut&, const NdbBlob&); diff --git a/storage/ndb/include/ndbapi/NdbScanOperation.hpp b/storage/ndb/include/ndbapi/NdbScanOperation.hpp index fde232e706c..0c1fe6d4eaf 100644 --- a/storage/ndb/include/ndbapi/NdbScanOperation.hpp +++ b/storage/ndb/include/ndbapi/NdbScanOperation.hpp @@ -42,7 +42,7 @@ public: * readTuples. */ enum ScanFlag { - SF_TupScan = (1 << 16), // scan TUP - only LM_CommittedRead + SF_TupScan = (1 << 16), // scan TUP SF_OrderBy = (1 << 24), // index scan in order SF_Descending = (2 << 24), // index scan in descending order SF_ReadRangeNo = (4 << 24), // enable @ref get_range_no diff --git a/storage/ndb/src/mgmapi/mgmapi.cpp b/storage/ndb/src/mgmapi/mgmapi.cpp index 6dfb48667aa..f184f089f2d 100644 --- a/storage/ndb/src/mgmapi/mgmapi.cpp +++ b/storage/ndb/src/mgmapi/mgmapi.cpp @@ -27,6 +27,7 @@ #include #include "mgmapi_configuration.hpp" #include +#include #include #include @@ -103,6 +104,9 @@ struct ndb_mgm_handle { #endif FILE *errstream; char *m_name; + int mgmd_version_major; + int mgmd_version_minor; + int mgmd_version_build; }; #define SET_ERROR(h, e, s) setError(h, e, __LINE__, s) @@ -174,6 +178,10 @@ ndb_mgm_create_handle() h->logfile = 0; #endif + h->mgmd_version_major= -1; + h->mgmd_version_minor= -1; + h->mgmd_version_build= -1; + DBUG_PRINT("info", ("handle=0x%x", (UintPtr)h)); DBUG_RETURN(h); } @@ -367,8 +375,9 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow *command_reply, * Print some info about why the parser returns NULL */ fprintf(handle->errstream, - "Error in mgm protocol parser. cmd: >%s< status: %d curr: %d\n", - cmd, (Uint32)ctx.m_status, ctx.m_currentToken); + "Error in mgm protocol parser. cmd: >%s< status: %d curr: %s\n", + cmd, (Uint32)ctx.m_status, + (ctx.m_currentToken)?ctx.m_currentToken:"NULL"); DBUG_PRINT("info",("ctx.status: %d, ctx.m_currentToken: %s", ctx.m_status, ctx.m_currentToken)); } @@ -684,7 +693,11 @@ ndb_mgm_get_status(NdbMgmHandle handle) out.println(""); char buf[1024]; - in.gets(buf, sizeof(buf)); + if(!in.gets(buf, sizeof(buf))) + { + SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_REPLY, "Probably disconnected"); + return NULL; + } if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; @@ -693,7 +706,11 @@ ndb_mgm_get_status(NdbMgmHandle handle) return NULL; } - in.gets(buf, sizeof(buf)); + if(!in.gets(buf, sizeof(buf))) + { + SET_ERROR(handle, NDB_MGM_ILLEGAL_SERVER_REPLY, "Probably disconnected"); + return NULL; + } if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; @@ -716,6 +733,13 @@ ndb_mgm_get_status(NdbMgmHandle handle) malloc(sizeof(ndb_mgm_cluster_state)+ noOfNodes*(sizeof(ndb_mgm_node_state)+sizeof("000.000.000.000#"))); + if(!state) + { + SET_ERROR(handle, NDB_MGM_OUT_OF_MEMORY, + "Allocating ndb_mgm_cluster_state"); + return NULL; + } + state->no_of_nodes= noOfNodes; ndb_mgm_node_state * ptr = &state->node_states[0]; int nodeId = 0; @@ -725,7 +749,13 @@ ndb_mgm_get_status(NdbMgmHandle handle) } i = -1; ptr--; for(; i stop_reply[] = { + int disconnect; + return ndb_mgm_stop3(handle, no_of_nodes, node_list, abort, &disconnect); +} + + +extern "C" +int +ndb_mgm_stop3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, + int abort, int *disconnect) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_stop3"); + const ParserRow stop_reply_v1[] = { MGM_CMD("stop reply", NULL, ""), MGM_ARG("stopped", Int, Optional, "No of stopped nodes"), MGM_ARG("result", String, Mandatory, "Error message"), MGM_END() }; + const ParserRow stop_reply_v2[] = { + MGM_CMD("stop reply", NULL, ""), + MGM_ARG("stopped", Int, Optional, "No of stopped nodes"), + MGM_ARG("result", String, Mandatory, "Error message"), + MGM_ARG("disconnect", Int, Mandatory, "Need to disconnect"), + MGM_END() + }; + CHECK_HANDLE(handle, -1); CHECK_CONNECTED(handle, -1); - if(no_of_nodes < 0){ + if(handle->mgmd_version_build==-1) + { + char verstr[50]; + if(!ndb_mgm_get_version(handle, + &(handle->mgmd_version_major), + &(handle->mgmd_version_minor), + &(handle->mgmd_version_build), + sizeof(verstr), + verstr)) + { + return -1; + } + } + int use_v2= ((handle->mgmd_version_major==5) + && ( + (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) + ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) + ||(handle->mgmd_version_minor>1) + ) + ) + || (handle->mgmd_version_major>5); + + if(no_of_nodes < -1){ SET_ERROR(handle, NDB_MGM_ILLEGAL_NUMBER_OF_NODES, "Negative number of nodes requested to stop"); return -1; } Uint32 stoppedNoOfNodes = 0; - if(no_of_nodes == 0){ + if(no_of_nodes <= 0){ /** - * All database nodes should be stopped + * All nodes should be stopped (all or just db) */ Properties args; args.put("abort", abort); + if(use_v2) + args.put("stop", (no_of_nodes==-1)?"mgm,db":"db"); const Properties *reply; - reply = ndb_mgm_call(handle, stop_reply, "stop all", &args); + if(use_v2) + reply = ndb_mgm_call(handle, stop_reply_v2, "stop all", &args); + else + reply = ndb_mgm_call(handle, stop_reply_v1, "stop all", &args); CHECK_REPLY(reply, -1); if(!reply->get("stopped", &stoppedNoOfNodes)){ @@ -874,6 +948,10 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, delete reply; return -1; } + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; BaseString result; reply->get("result", result); if(strcmp(result.c_str(), "Ok") != 0) { @@ -899,7 +977,11 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, args.put("abort", abort); const Properties *reply; - reply = ndb_mgm_call(handle, stop_reply, "stop", &args); + if(use_v2) + reply = ndb_mgm_call(handle, stop_reply_v2, "stop v2", &args); + else + reply = ndb_mgm_call(handle, stop_reply_v1, "stop", &args); + CHECK_REPLY(reply, stoppedNoOfNodes); if(!reply->get("stopped", &stoppedNoOfNodes)){ SET_ERROR(handle, NDB_MGM_STOP_FAILED, @@ -907,6 +989,10 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, delete reply; return -1; } + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; BaseString result; reply->get("result", result); if(strcmp(result.c_str(), "Ok") != 0) { @@ -918,22 +1004,71 @@ ndb_mgm_stop2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, return stoppedNoOfNodes; } +extern "C" +int +ndb_mgm_restart(NdbMgmHandle handle, int no_of_nodes, const int *node_list) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart"); + return ndb_mgm_restart2(handle, no_of_nodes, node_list, 0, 0, 0); +} + extern "C" int ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, int initial, int nostart, int abort) { - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart2"); + int disconnect; + + return ndb_mgm_restart3(handle, no_of_nodes, node_list, initial, nostart, + abort, &disconnect); +} + +extern "C" +int +ndb_mgm_restart3(NdbMgmHandle handle, int no_of_nodes, const int * node_list, + int initial, int nostart, int abort, int *disconnect) +{ + SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart3"); Uint32 restarted = 0; - const ParserRow restart_reply[] = { + const ParserRow restart_reply_v1[] = { MGM_CMD("restart reply", NULL, ""), MGM_ARG("result", String, Mandatory, "Error message"), MGM_ARG("restarted", Int, Optional, "No of restarted nodes"), MGM_END() }; + const ParserRow restart_reply_v2[] = { + MGM_CMD("restart reply", NULL, ""), + MGM_ARG("result", String, Mandatory, "Error message"), + MGM_ARG("restarted", Int, Optional, "No of restarted nodes"), + MGM_ARG("disconnect", Int, Optional, "Disconnect to apply"), + MGM_END() + }; + CHECK_HANDLE(handle, -1); CHECK_CONNECTED(handle, -1); - + + if(handle->mgmd_version_build==-1) + { + char verstr[50]; + if(!ndb_mgm_get_version(handle, + &(handle->mgmd_version_major), + &(handle->mgmd_version_minor), + &(handle->mgmd_version_build), + sizeof(verstr), + verstr)) + { + return -1; + } + } + int use_v2= ((handle->mgmd_version_major==5) + && ( + (handle->mgmd_version_minor==0 && handle->mgmd_version_build>=21) + ||(handle->mgmd_version_minor==1 && handle->mgmd_version_build>=12) + ||(handle->mgmd_version_minor>1) + ) + ) + || (handle->mgmd_version_major>5); + if(no_of_nodes < 0){ SET_ERROR(handle, NDB_MGM_RESTART_FAILED, "Restart requested of negative number of nodes"); @@ -948,7 +1083,7 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, const Properties *reply; const int timeout = handle->read_timeout; handle->read_timeout= 5*60*1000; // 5 minutes - reply = ndb_mgm_call(handle, restart_reply, "restart all", &args); + reply = ndb_mgm_call(handle, restart_reply_v1, "restart all", &args); handle->read_timeout= timeout; CHECK_REPLY(reply, -1); @@ -984,7 +1119,10 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, const Properties *reply; const int timeout = handle->read_timeout; handle->read_timeout= 5*60*1000; // 5 minutes - reply = ndb_mgm_call(handle, restart_reply, "restart node", &args); + if(use_v2) + reply = ndb_mgm_call(handle, restart_reply_v2, "restart node v2", &args); + else + reply = ndb_mgm_call(handle, restart_reply_v1, "restart node", &args); handle->read_timeout= timeout; if(reply != NULL) { BaseString result; @@ -995,20 +1133,16 @@ ndb_mgm_restart2(NdbMgmHandle handle, int no_of_nodes, const int * node_list, return -1; } reply->get("restarted", &restarted); + if(use_v2) + reply->get("disconnect", (Uint32*)disconnect); + else + *disconnect= 0; delete reply; } return restarted; } -extern "C" -int -ndb_mgm_restart(NdbMgmHandle handle, int no_of_nodes, const int *node_list) -{ - SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_restart"); - return ndb_mgm_restart2(handle, no_of_nodes, node_list, 0, 0, 0); -} - static const char *clusterlog_severity_names[]= { "enabled", "debug", "info", "warning", "error", "critical", "alert" }; @@ -2340,4 +2474,56 @@ int ndb_mgm_end_session(NdbMgmHandle handle) DBUG_RETURN(0); } +extern "C" +int ndb_mgm_get_version(NdbMgmHandle handle, + int *major, int *minor, int *build, int len, char* str) +{ + DBUG_ENTER("ndb_mgm_get_version"); + CHECK_HANDLE(handle, 0); + CHECK_CONNECTED(handle, 0); + + Properties args; + + const ParserRow reply[]= { + MGM_CMD("version", NULL, ""), + MGM_ARG("id", Int, Mandatory, "ID"), + MGM_ARG("major", Int, Mandatory, "Major"), + MGM_ARG("minor", Int, Mandatory, "Minor"), + MGM_ARG("string", String, Mandatory, "String"), + MGM_END() + }; + + const Properties *prop; + prop = ndb_mgm_call(handle, reply, "get version", &args); + CHECK_REPLY(prop, 0); + + Uint32 id; + if(!prop->get("id",&id)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + *build= getBuild(id); + + if(!prop->get("major",(Uint32*)major)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + if(!prop->get("minor",(Uint32*)minor)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + BaseString result; + if(!prop->get("string", result)){ + fprintf(handle->errstream, "Unable to get value\n"); + return 0; + } + + strncpy(str, result.c_str(), len); + + delete prop; + DBUG_RETURN(1); +} + template class Vector*>; diff --git a/storage/ndb/src/mgmclient/CommandInterpreter.cpp b/storage/ndb/src/mgmclient/CommandInterpreter.cpp index ccd05abaaa1..c31efebbf1e 100644 --- a/storage/ndb/src/mgmclient/CommandInterpreter.cpp +++ b/storage/ndb/src/mgmclient/CommandInterpreter.cpp @@ -155,6 +155,7 @@ private: NdbMgmHandle m_mgmsrv; NdbMgmHandle m_mgmsrv2; + const char *m_constr; bool m_connected; int m_verbose; int try_reconnect; @@ -335,22 +336,7 @@ convert(const char* s, int& val) { CommandInterpreter::CommandInterpreter(const char *_host,int verbose) : m_verbose(verbose) { - m_mgmsrv = ndb_mgm_create_handle(); - if(m_mgmsrv == NULL) { - ndbout_c("Cannot create handle to management server."); - exit(-1); - } - m_mgmsrv2 = ndb_mgm_create_handle(); - if(m_mgmsrv2 == NULL) { - ndbout_c("Cannot create 2:nd handle to management server."); - exit(-1); - } - if (ndb_mgm_set_connectstring(m_mgmsrv, _host)) - { - printError(); - exit(-1); - } - + m_constr= _host; m_connected= false; m_event_thread= 0; try_reconnect = 0; @@ -362,8 +348,6 @@ CommandInterpreter::CommandInterpreter(const char *_host,int verbose) CommandInterpreter::~CommandInterpreter() { disconnect(); - ndb_mgm_destroy_handle(&m_mgmsrv); - ndb_mgm_destroy_handle(&m_mgmsrv2); } static bool @@ -385,15 +369,14 @@ emptyString(const char* s) void CommandInterpreter::printError() { - if (ndb_mgm_check_connection(m_mgmsrv)) - { - m_connected= false; - disconnect(); - } ndbout_c("* %5d: %s", ndb_mgm_get_latest_error(m_mgmsrv), ndb_mgm_get_latest_error_msg(m_mgmsrv)); ndbout_c("* %s", ndb_mgm_get_latest_error_desc(m_mgmsrv)); + if (ndb_mgm_check_connection(m_mgmsrv)) + { + disconnect(); + } } //***************************************************************************** @@ -437,78 +420,97 @@ event_thread_run(void* m) } bool -CommandInterpreter::connect() +CommandInterpreter::connect() { DBUG_ENTER("CommandInterpreter::connect"); - if(!m_connected) + + if(m_connected) + DBUG_RETURN(m_connected); + + m_mgmsrv = ndb_mgm_create_handle(); + if(m_mgmsrv == NULL) { + ndbout_c("Cannot create handle to management server."); + exit(-1); + } + m_mgmsrv2 = ndb_mgm_create_handle(); + if(m_mgmsrv2 == NULL) { + ndbout_c("Cannot create 2:nd handle to management server."); + exit(-1); + } + + if (ndb_mgm_set_connectstring(m_mgmsrv, m_constr)) { - if(!ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1)) + printError(); + exit(-1); + } + + if(ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1)) + DBUG_RETURN(m_connected); // couldn't connect, always false + + const char *host= ndb_mgm_get_connected_host(m_mgmsrv); + unsigned port= ndb_mgm_get_connected_port(m_mgmsrv); + BaseString constr; + constr.assfmt("%s:%d",host,port); + if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) && + !ndb_mgm_connect(m_mgmsrv2, try_reconnect-1, 5, 1)) + { + DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d", + host, port)); + assert(m_event_thread == 0); + assert(do_event_thread == 0); + do_event_thread= 0; + m_event_thread = NdbThread_Create(event_thread_run, + (void**)&m_mgmsrv2, + 32768, + "CommandInterpreted_event_thread", + NDB_THREAD_PRIO_LOW); + if (m_event_thread != 0) { - const char *host= ndb_mgm_get_connected_host(m_mgmsrv); - unsigned port= ndb_mgm_get_connected_port(m_mgmsrv); - BaseString constr; - constr.assfmt("%s:%d",host,port); - if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) && - !ndb_mgm_connect(m_mgmsrv2, try_reconnect-1, 5, 1)) + DBUG_PRINT("info",("Thread created ok, waiting for started...")); + int iter= 1000; // try for 30 seconds + while(do_event_thread == 0 && + iter-- > 0) + NdbSleep_MilliSleep(30); + } + if (m_event_thread == 0 || + do_event_thread == 0 || + do_event_thread == -1) + { + DBUG_PRINT("info",("Warning, event thread startup failed, " + "degraded printouts as result, errno=%d", + errno)); + printf("Warning, event thread startup failed, " + "degraded printouts as result, errno=%d\n", errno); + do_event_thread= 0; + if (m_event_thread) { - DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d", - host, port)); - assert(m_event_thread == 0); - assert(do_event_thread == 0); - do_event_thread= 0; - m_event_thread = NdbThread_Create(event_thread_run, - (void**)&m_mgmsrv2, - 32768, - "CommandInterpreted_event_thread", - NDB_THREAD_PRIO_LOW); - if (m_event_thread != 0) - { - DBUG_PRINT("info",("Thread created ok, waiting for started...")); - int iter= 1000; // try for 30 seconds - while(do_event_thread == 0 && - iter-- > 0) - NdbSleep_MilliSleep(30); - } - if (m_event_thread == 0 || - do_event_thread == 0 || - do_event_thread == -1) - { - DBUG_PRINT("info",("Warning, event thread startup failed, " - "degraded printouts as result, errno=%d", - errno)); - printf("Warning, event thread startup failed, " - "degraded printouts as result, errno=%d\n", errno); - do_event_thread= 0; - if (m_event_thread) - { - void *res; - NdbThread_WaitFor(m_event_thread, &res); - NdbThread_Destroy(&m_event_thread); - } - ndb_mgm_disconnect(m_mgmsrv2); - } - } - else - { - DBUG_PRINT("warning", - ("Could not do 2:nd connect to mgmtserver for event listening")); - DBUG_PRINT("info", ("code: %d, msg: %s", - ndb_mgm_get_latest_error(m_mgmsrv2), - ndb_mgm_get_latest_error_msg(m_mgmsrv2))); - printf("Warning, event connect failed, degraded printouts as result\n"); - printf("code: %d, msg: %s\n", - ndb_mgm_get_latest_error(m_mgmsrv2), - ndb_mgm_get_latest_error_msg(m_mgmsrv2)); - } - m_connected= true; - DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port)); - if (m_verbose) - { - printf("Connected to Management Server at: %s:%d\n", - host, port); + void *res; + NdbThread_WaitFor(m_event_thread, &res); + NdbThread_Destroy(&m_event_thread); } + ndb_mgm_disconnect(m_mgmsrv2); } } + else + { + DBUG_PRINT("warning", + ("Could not do 2:nd connect to mgmtserver for event listening")); + DBUG_PRINT("info", ("code: %d, msg: %s", + ndb_mgm_get_latest_error(m_mgmsrv2), + ndb_mgm_get_latest_error_msg(m_mgmsrv2))); + printf("Warning, event connect failed, degraded printouts as result\n"); + printf("code: %d, msg: %s\n", + ndb_mgm_get_latest_error(m_mgmsrv2), + ndb_mgm_get_latest_error_msg(m_mgmsrv2)); + } + m_connected= true; + DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port)); + if (m_verbose) + { + printf("Connected to Management Server at: %s:%d\n", + host, port); + } + DBUG_RETURN(m_connected); } @@ -516,20 +518,18 @@ bool CommandInterpreter::disconnect() { DBUG_ENTER("CommandInterpreter::disconnect"); + if (m_event_thread) { void *res; do_event_thread= 0; NdbThread_WaitFor(m_event_thread, &res); NdbThread_Destroy(&m_event_thread); m_event_thread= 0; - ndb_mgm_disconnect(m_mgmsrv2); + ndb_mgm_destroy_handle(&m_mgmsrv2); } if (m_connected) { - if (ndb_mgm_disconnect(m_mgmsrv) == -1) { - ndbout_c("Could not disconnect from management server"); - printError(); - } + ndb_mgm_destroy_handle(&m_mgmsrv); m_connected= false; } DBUG_RETURN(true); @@ -985,7 +985,8 @@ CommandInterpreter::executeShutdown(char* parameters) NdbAutoPtr ap1((char*)state); int result = 0; - result = ndb_mgm_stop(m_mgmsrv, 0, 0); + int need_disconnect; + result = ndb_mgm_stop3(m_mgmsrv, -1, 0, 0, &need_disconnect); if (result < 0) { ndbout << "Shutdown of NDB Cluster node(s) failed." << endl; printError(); @@ -994,28 +995,11 @@ CommandInterpreter::executeShutdown(char* parameters) ndbout << result << " NDB Cluster node(s) have shutdown." << endl; - int mgm_id= 0; - mgm_id= ndb_mgm_get_mgmd_nodeid(m_mgmsrv); - if (mgm_id == 0) - { - ndbout << "Unable to locate management server, " - << "shutdown manually with STOP" + if(need_disconnect) { + ndbout << "Disconnecting to allow management server to shutdown." << endl; - return 1; + disconnect(); } - - result = ndb_mgm_stop(m_mgmsrv, 1, &mgm_id); - if (result <= 0) { - ndbout << "Shutdown of NDB Cluster management server failed." << endl; - printError(); - if (result == 0) - return 1; - return result; - } - - m_connected= false; - disconnect(); - ndbout << "NDB Cluster management server shutdown." << endl; return 0; } @@ -1237,12 +1221,7 @@ CommandInterpreter::executeConnect(char* parameters) { disconnect(); if (!emptyString(parameters)) { - if (ndb_mgm_set_connectstring(m_mgmsrv, - BaseString(parameters).trim().c_str())) - { - printError(); - return; - } + m_constr= BaseString(parameters).trim().c_str(); } connect(); } @@ -1407,6 +1386,7 @@ CommandInterpreter::executeStop(Vector &command_list, unsigned command_pos, int *node_ids, int no_of_nodes) { + int need_disconnect; int abort= 0; for (; command_pos < command_list.size(); command_pos++) { @@ -1421,7 +1401,8 @@ CommandInterpreter::executeStop(Vector &command_list, return; } - int result= ndb_mgm_stop2(m_mgmsrv, no_of_nodes, node_ids, abort); + int result= ndb_mgm_stop3(m_mgmsrv, no_of_nodes, node_ids, abort, + &need_disconnect); if (result < 0) { ndbout_c("Shutdown failed."); @@ -1435,10 +1416,17 @@ CommandInterpreter::executeStop(Vector &command_list, { ndbout << "Node"; for (int i= 0; i < no_of_nodes; i++) - ndbout << " " << node_ids[i]; + ndbout << " " << node_ids[i]; ndbout_c(" has shutdown."); } } + + if(need_disconnect) + { + ndbout << "Disconnecting to allow Management Server to shutdown" << endl; + disconnect(); + } + } void @@ -1529,6 +1517,7 @@ CommandInterpreter::executeRestart(Vector &command_list, int nostart= 0; int initialstart= 0; int abort= 0; + int need_disconnect= 0; for (; command_pos < command_list.size(); command_pos++) { @@ -1553,9 +1542,9 @@ CommandInterpreter::executeRestart(Vector &command_list, return; } - result= ndb_mgm_restart2(m_mgmsrv, no_of_nodes, node_ids, - initialstart, nostart, abort); - + result= ndb_mgm_restart3(m_mgmsrv, no_of_nodes, node_ids, + initialstart, nostart, abort, &need_disconnect); + if (result <= 0) { ndbout_c("Restart failed."); printError(); @@ -1571,6 +1560,8 @@ CommandInterpreter::executeRestart(Vector &command_list, ndbout << " " << node_ids[i]; ndbout_c(" is being restarted"); } + if(need_disconnect) + disconnect(); } } diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp index 3e92a892ddc..511536d2fdd 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -60,9 +60,6 @@ #include -extern bool g_StopServer; -extern bool g_RestartServer; - //#define MGM_SRV_DEBUG #ifdef MGM_SRV_DEBUG #define DEBUG(x) do ndbout << x << endl; while(0) @@ -937,6 +934,13 @@ int MgmtSrvr::sendStopMgmd(NodeId nodeId, * client connection to that mgmd and stop it that way. * This allows us to stop mgm servers when there isn't any real * distributed communication up. + * + * node_ids.size()==0 means to stop all DB nodes. + * MGM nodes will *NOT* be stopped. + * + * If we work out we should be stopping or restarting ourselves, + * we return <0 in stopSelf for restart, >0 for stop + * and 0 for do nothing. */ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, @@ -946,7 +950,8 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, bool stop, bool restart, bool nostart, - bool initialStart) + bool initialStart, + int* stopSelf) { int error = 0; DBUG_ENTER("MgmtSrvr::sendSTOP_REQ"); @@ -995,12 +1000,13 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, NodeId nodeId= 0; int use_master_node= 0; int do_send= 0; - int do_stop_self= 0; + *stopSelf= 0; NdbNodeBitmask nodes_to_stop; { for (unsigned i= 0; i < node_ids.size(); i++) { nodeId= node_ids[i]; + ndbout << "asked to stop " << nodeId << endl; if (getNodeType(nodeId) != NDB_MGM_NODE_TYPE_MGM) nodes_to_stop.set(nodeId); else if (nodeId != getOwnNodeId()) @@ -1011,7 +1017,11 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, stoppedNodes.set(nodeId); } else - do_stop_self= 1;; + { + ndbout << "which is me" << endl; + *stopSelf= (restart)? -1 : 1; + stoppedNodes.set(nodeId); + } } } int no_of_nodes_to_stop= nodes_to_stop.count(); @@ -1044,14 +1054,6 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, nodes.set(nodeId); } } - nodeId= 0; - while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)) - { - if(nodeId==getOwnNodeId()) - continue; - if(sendStopMgmd(nodeId, abort, stop, restart, nostart, initialStart)==0) - stoppedNodes.set(nodeId); - } } // now wait for the replies @@ -1143,11 +1145,9 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, DBUG_RETURN(SEND_OR_RECEIVE_FAILED); } } - if (!error && do_stop_self) + if (error && *stopSelf) { - if (restart) - g_RestartServer= true; - g_StopServer= true; + *stopSelf= 0; } DBUG_RETURN(error); } @@ -1157,7 +1157,7 @@ int MgmtSrvr::sendSTOP_REQ(const Vector &node_ids, */ int MgmtSrvr::stopNodes(const Vector &node_ids, - int *stopCount, bool abort) + int *stopCount, bool abort, int* stopSelf) { if (!abort) { @@ -1179,20 +1179,46 @@ int MgmtSrvr::stopNodes(const Vector &node_ids, false, false, false, - false); + false, + stopSelf); if (stopCount) *stopCount= nodes.count(); return ret; } +int MgmtSrvr::shutdownMGM(int *stopCount, bool abort, int *stopSelf) +{ + NodeId nodeId = 0; + int error; + + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)) + { + if(nodeId==getOwnNodeId()) + continue; + error= sendStopMgmd(nodeId, abort, true, false, + false, false); + if (error == 0) + *stopCount++; + } + + *stopSelf= 1; + *stopCount++; + + return 0; +} + /* - * Perform system shutdown + * Perform DB nodes shutdown. + * MGM servers are left in their current state */ -int MgmtSrvr::stop(int * stopCount, bool abort) +int MgmtSrvr::shutdownDB(int * stopCount, bool abort) { NodeBitmask nodes; Vector node_ids; + + int tmp; + int ret = sendSTOP_REQ(node_ids, nodes, 0, @@ -1200,7 +1226,8 @@ int MgmtSrvr::stop(int * stopCount, bool abort) true, false, false, - false); + false, + &tmp); if (stopCount) *stopCount = nodes.count(); return ret; @@ -1225,6 +1252,7 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) } NodeBitmask nodes; Vector node_ids; + int stopSelf; int ret = sendSTOP_REQ(node_ids, nodes, singleUserNodeId, @@ -1232,7 +1260,8 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) false, false, false, - false); + false, + &stopSelf); if (stopCount) *stopCount = nodes.count(); return ret; @@ -1244,7 +1273,8 @@ int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) int MgmtSrvr::restartNodes(const Vector &node_ids, int * stopCount, bool nostart, - bool initialStart, bool abort) + bool initialStart, bool abort, + int *stopSelf) { NodeBitmask nodes; int ret= sendSTOP_REQ(node_ids, @@ -1254,7 +1284,8 @@ int MgmtSrvr::restartNodes(const Vector &node_ids, false, true, true, - initialStart); + initialStart, + stopSelf); if (ret) return ret; @@ -1297,14 +1328,16 @@ int MgmtSrvr::restartNodes(const Vector &node_ids, } /* - * Perform system restart + * Perform restart of all DB nodes */ -int MgmtSrvr::restart(bool nostart, bool initialStart, - bool abort, int * stopCount ) +int MgmtSrvr::restartDB(bool nostart, bool initialStart, + bool abort, int * stopCount) { NodeBitmask nodes; Vector node_ids; + int tmp; + int ret = sendSTOP_REQ(node_ids, nodes, 0, @@ -1312,7 +1345,8 @@ int MgmtSrvr::restart(bool nostart, bool initialStart, true, true, true, - initialStart); + initialStart, + &tmp); if (ret) return ret; diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp index 80eef3b1af2..a00ba2cda12 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -255,12 +255,15 @@ public: * @param processId: Id of the DB process to stop * @return 0 if succeeded, otherwise: as stated above, plus: */ - int stopNodes(const Vector &node_ids, int *stopCount, bool abort); + int stopNodes(const Vector &node_ids, int *stopCount, bool abort, + int *stopSelf); + + int shutdownMGM(int *stopCount, bool abort, int *stopSelf); /** - * Stop the system + * shutdown the DB nodes */ - int stop(int * cnt = 0, bool abort = false); + int shutdownDB(int * cnt = 0, bool abort = false); /** * print version info about a node @@ -294,14 +297,14 @@ public: */ int restartNodes(const Vector &node_ids, int *stopCount, bool nostart, - bool initialStart, bool abort); + bool initialStart, bool abort, int *stopSelf); /** - * Restart the system + * Restart all DB nodes */ - int restart(bool nostart, bool initialStart, - bool abort = false, - int * stopCount = 0); + int restartDB(bool nostart, bool initialStart, + bool abort = false, + int * stopCount = 0); struct BackupEvent { enum Event { @@ -500,7 +503,8 @@ private: bool stop, bool restart, bool nostart, - bool initialStart); + bool initialStart, + int *stopSelf); /** * Check if it is possible to send a signal to a (DB) process diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp index beec5a819b7..cc7892f8b36 100644 --- a/storage/ndb/src/mgmsrv/Services.cpp +++ b/storage/ndb/src/mgmsrv/Services.cpp @@ -35,6 +35,7 @@ #include extern bool g_StopServer; +extern bool g_RestartServer; extern EventLogger g_eventLogger; static const unsigned int MAX_READ_TIMEOUT = 1000 ; @@ -144,7 +145,13 @@ ParserRow commands[] = { MGM_CMD("get info clusterlog", &MgmApiSession::getInfoClusterLog, ""), - MGM_CMD("restart node", &MgmApiSession::restart, ""), + MGM_CMD("restart node", &MgmApiSession::restart_v1, ""), + MGM_ARG("node", String, Mandatory, "Nodes to restart"), + MGM_ARG("initialstart", Int, Optional, "Initial start"), + MGM_ARG("nostart", Int, Optional, "No start"), + MGM_ARG("abort", Int, Optional, "Abort"), + + MGM_CMD("restart node v2", &MgmApiSession::restart_v2, ""), MGM_ARG("node", String, Mandatory, "Nodes to restart"), MGM_ARG("initialstart", Int, Optional, "Initial start"), MGM_ARG("nostart", Int, Optional, "No start"), @@ -185,13 +192,18 @@ ParserRow commands[] = { MGM_CMD("abort backup", &MgmApiSession::abortBackup, ""), MGM_ARG("id", Int, Mandatory, "Backup id"), - MGM_CMD("stop", &MgmApiSession::stop, ""), + MGM_CMD("stop", &MgmApiSession::stop_v1, ""), + MGM_ARG("node", String, Mandatory, "Node"), + MGM_ARG("abort", Int, Mandatory, "Node"), + + MGM_CMD("stop v2", &MgmApiSession::stop_v2, ""), MGM_ARG("node", String, Mandatory, "Node"), MGM_ARG("abort", Int, Mandatory, "Node"), MGM_CMD("stop all", &MgmApiSession::stopAll, ""), MGM_ARG("abort", Int, Mandatory, "Node"), - + MGM_ARG("stop", String, Optional, "MGM/DB or both"), + MGM_CMD("enter single user", &MgmApiSession::enterSingleUser, ""), MGM_ARG("nodeId", Int, Mandatory, "Node"), @@ -276,6 +288,7 @@ MgmApiSession::MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock) m_output = new SocketOutputStream(sock); m_parser = new Parser_t(commands, *m_input, true, true, true); m_allocated_resources= new MgmtSrvr::Allocated_resources(m_mgmsrv); + m_stopSelf= 0; DBUG_VOID_RETURN; } @@ -295,6 +308,10 @@ MgmApiSession::~MgmApiSession() NDB_CLOSE_SOCKET(m_socket); m_socket= NDB_INVALID_SOCKET; } + if(m_stopSelf < 0) + g_RestartServer= true; + if(m_stopSelf) + g_StopServer= true; DBUG_VOID_RETURN; } @@ -875,8 +892,19 @@ MgmApiSession::stopSignalLog(Parser::Context &, } void -MgmApiSession::restart(Parser::Context &, +MgmApiSession::restart_v1(Parser::Context &, Properties const &args) { + restart(args,1); +} + +void +MgmApiSession::restart_v2(Parser::Context &, + Properties const &args) { + restart(args,2); +} + +void +MgmApiSession::restart(Properties const &args, int version) { Uint32 nostart = 0, initialstart = 0, @@ -901,7 +929,8 @@ MgmApiSession::restart(Parser::Context &, &restarted, nostart != 0, initialstart != 0, - abort != 0); + abort != 0, + &m_stopSelf); m_output->println("restart reply"); if(result != 0){ @@ -909,6 +938,8 @@ MgmApiSession::restart(Parser::Context &, } else m_output->println("result: Ok"); m_output->println("restarted: %d", restarted); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } @@ -925,7 +956,7 @@ MgmApiSession::restartAll(Parser::Context &, args.get("nostart", &nostart); int count = 0; - int result = m_mgmsrv.restart(nostart, initialstart, abort, &count); + int result = m_mgmsrv.restartDB(nostart, initialstart, abort, &count); m_output->println("restart reply"); if(result != 0) @@ -1018,8 +1049,19 @@ MgmApiSession::getInfoClusterLog(Parser::Context &, } void -MgmApiSession::stop(Parser::Context &, - Properties const &args) { +MgmApiSession::stop_v1(Parser::Context &, + Properties const &args) { + stop(args,1); +} + +void +MgmApiSession::stop_v2(Parser::Context &, + Properties const &args) { + stop(args,2); +} + +void +MgmApiSession::stop(Properties const &args, int version) { Uint32 abort; char *nodes_str; Vector nodes; @@ -1044,7 +1086,7 @@ MgmApiSession::stop(Parser::Context &, int stopped= 0; int result= 0; if (nodes.size()) - result= m_mgmsrv.stopNodes(nodes, &stopped, abort != 0); + result= m_mgmsrv.stopNodes(nodes, &stopped, abort != 0, &m_stopSelf); m_output->println("stop reply"); if(result != 0) @@ -1052,25 +1094,41 @@ MgmApiSession::stop(Parser::Context &, else m_output->println("result: Ok"); m_output->println("stopped: %d", stopped); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } - void MgmApiSession::stopAll(Parser::Context &, - Properties const &args) { - int stopped = 0; + Properties const &args) { + int stopped[2] = {0,0}; Uint32 abort; args.get("abort", &abort); - int result = m_mgmsrv.stop(&stopped, abort != 0); + BaseString stop; + const char* tostop= "db"; + int ver=1; + if (args.get("stop", stop)) + { + tostop= stop.c_str(); + ver= 2; + } + + int result= 0; + if(strstr(tostop,"db")) + result= m_mgmsrv.shutdownDB(&stopped[0], abort != 0); + if(!result && strstr(tostop,"mgm")) + result= m_mgmsrv.shutdownMGM(&stopped[1], abort!=0, &m_stopSelf); m_output->println("stop reply"); if(result != 0) m_output->println("result: %s", get_error_text(result)); else m_output->println("result: Ok"); - m_output->println("stopped: %d", stopped); + m_output->println("stopped: %d", stopped[0]+stopped[1]); + if(ver >1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); m_output->println(""); } diff --git a/storage/ndb/src/mgmsrv/Services.hpp b/storage/ndb/src/mgmsrv/Services.hpp index abe0233cb33..d0a61b3f91d 100644 --- a/storage/ndb/src/mgmsrv/Services.hpp +++ b/storage/ndb/src/mgmsrv/Services.hpp @@ -41,6 +41,7 @@ private: Parser_t *m_parser; MgmtSrvr::Allocated_resources *m_allocated_resources; char m_err_str[1024]; + int m_stopSelf; // -1 is restart, 0 do nothing, 1 stop void getConfig_common(Parser_t::Context &ctx, const class Properties &args, @@ -62,7 +63,9 @@ public: void getVersion(Parser_t::Context &ctx, const class Properties &args); void getStatus(Parser_t::Context &ctx, const class Properties &args); void getInfoClusterLog(Parser_t::Context &ctx, const class Properties &args); - void restart(Parser_t::Context &ctx, const class Properties &args); + void restart(const class Properties &args, int version); + void restart_v1(Parser_t::Context &ctx, const class Properties &args); + void restart_v2(Parser_t::Context &ctx, const class Properties &args); void restartAll(Parser_t::Context &ctx, const class Properties &args); void insertError(Parser_t::Context &ctx, const class Properties &args); void setTrace(Parser_t::Context &ctx, const class Properties &args); @@ -74,7 +77,9 @@ public: void abortBackup(Parser_t::Context &ctx, const class Properties &args); void enterSingleUser(Parser_t::Context &ctx, const class Properties &args); void exitSingleUser(Parser_t::Context &ctx, const class Properties &args); - void stop(Parser_t::Context &ctx, const class Properties &args); + void stop_v1(Parser_t::Context &ctx, const class Properties &args); + void stop_v2(Parser_t::Context &ctx, const class Properties &args); + void stop(const class Properties &args, int version); void stopAll(Parser_t::Context &ctx, const class Properties &args); void start(Parser_t::Context &ctx, const class Properties &args); void startAll(Parser_t::Context &ctx, const class Properties &args); diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp index 4e3e13b59bf..081c7e2f995 100644 --- a/storage/ndb/src/ndbapi/NdbBlob.cpp +++ b/storage/ndb/src/ndbapi/NdbBlob.cpp @@ -393,6 +393,16 @@ NdbBlob::isScanOp() theNdbOp->theOperationType == NdbOperation::OpenRangeScanRequest; } +inline bool +NdbBlob::isReadOnlyOp() +{ + return ! ( + theNdbOp->theOperationType == NdbOperation::InsertRequest || + theNdbOp->theOperationType == NdbOperation::UpdateRequest || + theNdbOp->theOperationType == NdbOperation::WriteRequest + ); +} + inline bool NdbBlob::isTakeOverOp() { @@ -638,12 +648,12 @@ NdbBlob::getValue(void* data, Uint32 bytes) { DBUG_ENTER("NdbBlob::getValue"); DBUG_PRINT("info", ("data=%p bytes=%u", data, bytes)); - if (theGetFlag || theState != Prepared) { - setErrorCode(NdbBlobImpl::ErrState); + if (! isReadOp() && ! isScanOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); DBUG_RETURN(-1); } - if (! isReadOp() && ! isScanOp()) { - setErrorCode(NdbBlobImpl::ErrUsage); + if (theGetFlag || theState != Prepared) { + setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); } if (data == NULL && bytes != 0) { @@ -661,12 +671,12 @@ NdbBlob::setValue(const void* data, Uint32 bytes) { DBUG_ENTER("NdbBlob::setValue"); DBUG_PRINT("info", ("data=%p bytes=%u", data, bytes)); - if (theSetFlag || theState != Prepared) { - setErrorCode(NdbBlobImpl::ErrState); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); DBUG_RETURN(-1); } - if (! isInsertOp() && ! isUpdateOp() && ! isWriteOp()) { - setErrorCode(NdbBlobImpl::ErrUsage); + if (theSetFlag || theState != Prepared) { + setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); } if (data == NULL && bytes != 0) { @@ -762,6 +772,10 @@ int NdbBlob::setNull() { DBUG_ENTER("NdbBlob::setNull"); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theNullFlag == -1) { if (theState == Prepared) { DBUG_RETURN(setValue(0, 0)); @@ -800,6 +814,10 @@ NdbBlob::truncate(Uint64 length) { DBUG_ENTER("NdbBlob::truncate"); DBUG_PRINT("info", ("length=%llu", length)); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theNullFlag == -1) { setErrorCode(NdbBlobImpl::ErrState); DBUG_RETURN(-1); @@ -857,12 +875,14 @@ NdbBlob::setPos(Uint64 pos) int NdbBlob::readData(void* data, Uint32& bytes) { + DBUG_ENTER("NdbBlob::readData"); if (theState != Active) { setErrorCode(NdbBlobImpl::ErrState); - return -1; + DBUG_RETURN(-1); } char* buf = static_cast(data); - return readDataPrivate(buf, bytes); + int ret = readDataPrivate(buf, bytes); + DBUG_RETURN(ret); } int @@ -951,12 +971,18 @@ NdbBlob::readDataPrivate(char* buf, Uint32& bytes) int NdbBlob::writeData(const void* data, Uint32 bytes) { + DBUG_ENTER("NdbBlob::writeData"); + if (isReadOnlyOp()) { + setErrorCode(NdbBlobImpl::ErrCompat); + DBUG_RETURN(-1); + } if (theState != Active) { setErrorCode(NdbBlobImpl::ErrState); - return -1; + DBUG_RETURN(-1); } const char* buf = static_cast(data); - return writeDataPrivate(buf, bytes); + int ret = writeDataPrivate(buf, bytes); + DBUG_RETURN(ret); } int @@ -1355,6 +1381,9 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(-1); } if (isReadOp()) { + // upgrade lock mode + if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead) + theNdbOp->theLockMode = NdbOperation::LM_Read; // add read of head+inline in this op if (getHeadInlineValue(theNdbOp) == -1) DBUG_RETURN(-1); @@ -1373,6 +1402,9 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl supportedOp = true; } if (isScanOp()) { + // upgrade lock mode + if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead) + theNdbOp->theLockMode = NdbOperation::LM_Read; // add read of head+inline in this op if (getHeadInlineValue(theNdbOp) == -1) DBUG_RETURN(-1); diff --git a/storage/ndb/src/ndbapi/NdbBlobImpl.hpp b/storage/ndb/src/ndbapi/NdbBlobImpl.hpp index e6b41c964e6..b83b1d1ad99 100644 --- a/storage/ndb/src/ndbapi/NdbBlobImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbBlobImpl.hpp @@ -24,7 +24,7 @@ public: STATIC_CONST( ErrTable = 4263 ); // "Invalid usage of blob attribute" STATIC_CONST( ErrUsage = 4264 ); - // "Method is not valid in current blob state" + // "The blob method is not valid in current blob state" STATIC_CONST( ErrState = 4265 ); // "Invalid blob seek position" STATIC_CONST( ErrSeek = 4266 ); @@ -33,9 +33,11 @@ public: // "Error in blob head update forced rollback of transaction" STATIC_CONST( ErrAbort = 4268 ); // "Unknown blob error" - STATIC_CONST( ErrUnknown = 4269 ); + STATIC_CONST( ErrUnknown = 4270 ); // "Corrupted main table PK in blob operation" STATIC_CONST( ErrCorruptPK = 4274 ); + // "The blob method is incompatible with operation type or lock mode" + STATIC_CONST( ErrCompat = 4275 ); }; #endif diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c index 22252960e21..a867ad7315b 100644 --- a/storage/ndb/src/ndbapi/ndberror.c +++ b/storage/ndb/src/ndbapi/ndberror.c @@ -594,7 +594,7 @@ ErrorBundle ErrorCodes[] = { { 4262, DMEC, UD, "NdbScanFilter: Condition is out of bounds"}, { 4263, DMEC, IE, "Invalid blob attributes or invalid blob parts table" }, { 4264, DMEC, AE, "Invalid usage of blob attribute" }, - { 4265, DMEC, AE, "Method is not valid in current blob state" }, + { 4265, DMEC, AE, "The method is not valid in current blob state" }, { 4266, DMEC, AE, "Invalid blob seek position" }, { 4267, DMEC, IE, "Corrupted blob value" }, { 4268, DMEC, IE, "Error in blob head update forced rollback of transaction" }, @@ -604,7 +604,8 @@ ErrorBundle ErrorCodes[] = { { 4271, DMEC, AE, "Invalid index object, not retrieved via getIndex()" }, { 4272, DMEC, AE, "Table definition has undefined column" }, { 4273, DMEC, IE, "No blob table in dict cache" }, - { 4274, DMEC, IE, "Corrupted main table PK in blob operation" } + { 4274, DMEC, IE, "Corrupted main table PK in blob operation" }, + { 4275, DMEC, AE, "The blob method is incompatible with operation type or lock mode" } }; static diff --git a/storage/ndb/test/ndbapi/testBlobs.cpp b/storage/ndb/test/ndbapi/testBlobs.cpp index bf9a8b1bce9..fae3a662ff9 100644 --- a/storage/ndb/test/ndbapi/testBlobs.cpp +++ b/storage/ndb/test/ndbapi/testBlobs.cpp @@ -297,13 +297,15 @@ createTable() struct Bval { char* m_val; unsigned m_len; - char* m_buf; + char* m_buf; // read/write buffer unsigned m_buflen; + int m_error_code; // for testing expected error code Bval() : m_val(0), m_len(0), - m_buf(0), // read/write buffer - m_buflen(0) + m_buf(0), + m_buflen(0), + m_error_code(0) {} ~Bval() { delete [] m_val; delete [] m_buf; } void alloc(unsigned buflen) { @@ -459,19 +461,23 @@ getBlobLength(NdbBlob* h, unsigned& len) // setValue / getValue static int -setBlobValue(NdbBlob* h, const Bval& v) +setBlobValue(NdbBlob* h, const Bval& v, int error_code = 0) { bool null = (v.m_val == 0); bool isNull; unsigned len; DBG("setValue " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null); if (null) { - CHK(h->setNull() == 0); + CHK(h->setNull() == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; isNull = false; CHK(h->getNull(isNull) == 0 && isNull == true); CHK(getBlobLength(h, len) == 0 && len == 0); } else { - CHK(h->setValue(v.m_val, v.m_len) == 0); + CHK(h->setValue(v.m_val, v.m_len) == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; CHK(h->getNull(isNull) == 0 && isNull == false); CHK(getBlobLength(h, len) == 0 && len == v.m_len); } @@ -479,11 +485,11 @@ setBlobValue(NdbBlob* h, const Bval& v) } static int -setBlobValue(const Tup& tup) +setBlobValue(const Tup& tup, int error_code = 0) { - CHK(setBlobValue(g_bh1, tup.m_blob1) == 0); + CHK(setBlobValue(g_bh1, tup.m_blob1, error_code) == 0); if (! g_opt.m_oneblob) - CHK(setBlobValue(g_bh2, tup.m_blob2) == 0); + CHK(setBlobValue(g_bh2, tup.m_blob2, error_code) == 0); return 0; } @@ -543,13 +549,18 @@ writeBlobData(NdbBlob* h, const Bval& v) bool isNull; unsigned len; DBG("write " << h->getColumn()->getName() << " len=" << v.m_len << " null=" << null); + int error_code = v.m_error_code; if (null) { - CHK(h->setNull() == 0); + CHK(h->setNull() == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; isNull = false; CHK(h->getNull(isNull) == 0 && isNull == true); CHK(getBlobLength(h, len) == 0 && len == 0); } else { - CHK(h->truncate(v.m_len) == 0); + CHK(h->truncate(v.m_len) == 0 || h->getNdbError().code == error_code); + if (error_code) + return 0; unsigned n = 0; do { unsigned m = g_opt.m_full ? v.m_len : urandom(v.m_len + 1); @@ -568,11 +579,14 @@ writeBlobData(NdbBlob* h, const Bval& v) } static int -writeBlobData(const Tup& tup) +writeBlobData(Tup& tup, int error_code = 0) { + tup.m_blob1.m_error_code = error_code; CHK(writeBlobData(g_bh1, tup.m_blob1) == 0); - if (! g_opt.m_oneblob) + if (! g_opt.m_oneblob) { + tup.m_blob2.m_error_code = error_code; CHK(writeBlobData(g_bh2, tup.m_blob2) == 0); + } return 0; } @@ -635,19 +649,20 @@ blobWriteHook(NdbBlob* h, void* arg) } static int -setBlobWriteHook(NdbBlob* h, Bval& v) +setBlobWriteHook(NdbBlob* h, Bval& v, int error_code = 0) { DBG("setBlobWriteHook"); + v.m_error_code = error_code; CHK(h->setActiveHook(blobWriteHook, &v) == 0); return 0; } static int -setBlobWriteHook(Tup& tup) +setBlobWriteHook(Tup& tup, int error_code = 0) { - CHK(setBlobWriteHook(g_bh1, tup.m_blob1) == 0); + CHK(setBlobWriteHook(g_bh1, tup.m_blob1, error_code) == 0); if (! g_opt.m_oneblob) - CHK(setBlobWriteHook(g_bh2, tup.m_blob2) == 0); + CHK(setBlobWriteHook(g_bh2, tup.m_blob2, error_code) == 0); return 0; } @@ -869,7 +884,10 @@ readPk(int style) DBG("readPk pk1=" << hex << tup.m_pk1); CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); - CHK(g_opr->readTuple() == 0); + if (urandom(2) == 0) + CHK(g_opr->readTuple() == 0); + else + CHK(g_opr->readTuple(NdbOperation::LM_CommittedRead) == 0); CHK(g_opr->equal("PK1", tup.m_pk1) == 0); if (g_opt.m_pk2len != 0) CHK(g_opr->equal("PK2", tup.m_pk2) == 0); @@ -883,6 +901,8 @@ readPk(int style) CHK(readBlobData(tup) == 0); } CHK(g_con->execute(Commit) == 0); + // verify lock mode upgrade + CHK(g_opr->getLockMode() == NdbOperation::LM_Read); if (style == 0 || style == 1) { CHK(verifyBlobValue(tup) == 0); } @@ -900,23 +920,40 @@ updatePk(int style) for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("updatePk pk1=" << hex << tup.m_pk1); - CHK((g_con = g_ndb->startTransaction()) != 0); - CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); - CHK(g_opr->updateTuple() == 0); - CHK(g_opr->equal("PK1", tup.m_pk1) == 0); - if (g_opt.m_pk2len != 0) - CHK(g_opr->equal("PK2", tup.m_pk2) == 0); - CHK(getBlobHandles(g_opr) == 0); - if (style == 0) { - CHK(setBlobValue(tup) == 0); - } else if (style == 1) { - CHK(setBlobWriteHook(tup) == 0); - } else { - CHK(g_con->execute(NoCommit) == 0); - CHK(writeBlobData(tup) == 0); + while (1) { + int mode = urandom(3); + int error_code = mode == 0 ? 0 : 4275; + CHK((g_con = g_ndb->startTransaction()) != 0); + CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0); + if (mode == 0) { + DBG("using updateTuple"); + CHK(g_opr->updateTuple() == 0); + } else if (mode == 1) { + DBG("using readTuple exclusive"); + CHK(g_opr->readTuple(NdbOperation::LM_Exclusive) == 0); + } else { + DBG("using readTuple - will fail and retry"); + CHK(g_opr->readTuple() == 0); + } + CHK(g_opr->equal("PK1", tup.m_pk1) == 0); + if (g_opt.m_pk2len != 0) + CHK(g_opr->equal("PK2", tup.m_pk2) == 0); + CHK(getBlobHandles(g_opr) == 0); + if (style == 0) { + CHK(setBlobValue(tup, error_code) == 0); + } else if (style == 1) { + CHK(setBlobWriteHook(tup, error_code) == 0); + } else { + CHK(g_con->execute(NoCommit) == 0); + CHK(writeBlobData(tup, error_code) == 0); + } + if (error_code == 0) { + CHK(g_con->execute(Commit) == 0); + g_ndb->closeTransaction(g_con); + break; + } + g_ndb->closeTransaction(g_con); } - CHK(g_con->execute(Commit) == 0); - g_ndb->closeTransaction(g_con); g_opr = 0; g_con = 0; tup.m_exists = true; @@ -1002,7 +1039,10 @@ readIdx(int style) DBG("readIdx pk1=" << hex << tup.m_pk1); CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0); - CHK(g_opx->readTuple() == 0); + if (urandom(2) == 0) + CHK(g_opx->readTuple() == 0); + else + CHK(g_opx->readTuple(NdbOperation::LM_CommittedRead) == 0); CHK(g_opx->equal("PK2", tup.m_pk2) == 0); CHK(getBlobHandles(g_opx) == 0); if (style == 0) { @@ -1014,6 +1054,8 @@ readIdx(int style) CHK(readBlobData(tup) == 0); } CHK(g_con->execute(Commit) == 0); + // verify lock mode upgrade (already done by NdbIndexOperation) + CHK(g_opx->getLockMode() == NdbOperation::LM_Read); if (style == 0 || style == 1) { CHK(verifyBlobValue(tup) == 0); } @@ -1031,6 +1073,7 @@ updateIdx(int style) for (unsigned k = 0; k < g_opt.m_rows; k++) { Tup& tup = g_tups[k]; DBG("updateIdx pk1=" << hex << tup.m_pk1); + // skip 4275 testing CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_opx = g_con->getNdbIndexOperation(g_opt.m_x1name, g_opt.m_tname)) != 0); CHK(g_opx->updateTuple() == 0); @@ -1128,7 +1171,10 @@ readScan(int style, bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + if (urandom(2) == 0) + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); + else + CHK(g_ops->readTuples(NdbOperation::LM_CommittedRead) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1139,6 +1185,8 @@ readScan(int style, bool idx) CHK(setBlobReadHook(tup) == 0); } CHK(g_con->execute(NoCommit) == 0); + // verify lock mode upgrade + CHK(g_ops->getLockMode() == NdbOperation::LM_Read); unsigned rows = 0; while (1) { int ret; @@ -1180,7 +1228,7 @@ updateScan(int style, bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1199,6 +1247,7 @@ updateScan(int style, bool idx) // calculate new blob values calcBval(g_tups[k], false); tup.copyfrom(g_tups[k]); + // cannot do 4275 testing, scan op error code controls execution CHK((g_opr = g_ops->updateCurrentTuple()) != 0); CHK(getBlobHandles(g_opr) == 0); if (style == 0) { @@ -1232,7 +1281,7 @@ deleteScan(bool idx) } else { CHK((g_ops = g_con->getNdbIndexScanOperation(g_opt.m_x2name, g_opt.m_tname)) != 0); } - CHK(g_ops->readTuples(NdbScanOperation::LM_Exclusive) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Exclusive) == 0); CHK(g_ops->getValue("PK1", (char*)&tup.m_pk1) != 0); if (g_opt.m_pk2len != 0) CHK(g_ops->getValue("PK2", tup.m_pk2) != 0); @@ -1651,7 +1700,7 @@ testperf() char b[20]; CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); CHK(g_ops->getValue(cA, (char*)&a) != 0); CHK(g_ops->getValue(cB, b) != 0); CHK(g_con->execute(NoCommit) == 0); @@ -1680,7 +1729,7 @@ testperf() char c[20]; CHK((g_con = g_ndb->startTransaction()) != 0); CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); - CHK(g_ops->readTuples(NdbScanOperation::LM_Read) == 0); + CHK(g_ops->readTuples(NdbOperation::LM_Read) == 0); CHK(g_ops->getValue(cA, (char*)&a) != 0); CHK((g_bh1 = g_ops->getBlobHandle(cC)) != 0); CHK(g_con->execute(NoCommit) == 0); diff --git a/unittest/examples/no_plan-t.c b/unittest/examples/no_plan-t.c index 98e4d06def6..a40b46dc278 100644 --- a/unittest/examples/no_plan-t.c +++ b/unittest/examples/no_plan-t.c @@ -14,8 +14,8 @@ tests are skipped for an unknown reason. */ int main() { - ok(1, NULL); - ok(1, NULL); - ok(1, NULL); + ok(1, " "); + ok(1, " "); + ok(1, " "); return exit_status(); } diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index b1e0356bac6..8e6255b9e68 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -2,10 +2,10 @@ AM_CPPFLAGS = @ZLIB_INCLUDES@ -I$(top_builddir)/include AM_CPPFLAGS += -I$(top_srcdir)/include -I$(top_srcdir)/unittest/mytap -AM_LDFLAGS = -L$(top_builddir)/unittest/mytap -L$(top_builddir)/mysys -AM_LDFLAGS += -L$(top_builddir)/strings -L$(top_builddir)/dbug - -LDADD = -lmytap -lmysys -ldbug -lmystrings +LDADD = $(top_builddir)/unittest/mytap/libmytap.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/strings/libmystrings.a noinst_PROGRAMS = bitmap-t base64-t my_atomic-t diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c index 8a3fe129b07..71408ce957f 100644 --- a/unittest/mysys/my_atomic-t.c +++ b/unittest/mysys/my_atomic-t.c @@ -14,13 +14,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include - #include +#include #include #include -my_atomic_32_t a32,b32,c32; +int32 a32,b32,c32; my_atomic_rwlock_t rwl; pthread_attr_t thr_attr; @@ -36,8 +35,13 @@ pthread_handler_t test_atomic_add_handler(void *arg) for (x=((int)(&m)); m ; m--) { x=x*m+0x87654321; - my_atomic_add32(&a32, x, &rwl); - my_atomic_add32(&a32, -x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, x); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, -x); + my_atomic_rwlock_wrunlock(&rwl); } pthread_mutex_lock(&mutex); N--; @@ -57,17 +61,33 @@ pthread_handler_t test_atomic_add_handler(void *arg) pthread_handler_t test_atomic_swap_handler(void *arg) { int m=*(int *)arg; - uint32 x=my_atomic_add32(&b32, 1, &rwl); + int32 x; - my_atomic_add32(&a32, x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_add32(&b32, 1); + my_atomic_rwlock_wrunlock(&rwl); + + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, x); + my_atomic_rwlock_wrunlock(&rwl); for (; m ; m--) - x=my_atomic_swap32(&c32, x,&rwl); + { + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_swap32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } if (!x) - x=my_atomic_swap32(&c32, x,&rwl); + { + my_atomic_rwlock_wrlock(&rwl); + x=my_atomic_swap32(&c32, x); + my_atomic_rwlock_wrunlock(&rwl); + } - my_atomic_add32(&a32, -x, &rwl); + my_atomic_rwlock_wrlock(&rwl); + my_atomic_add32(&a32, -x); + my_atomic_rwlock_wrunlock(&rwl); pthread_mutex_lock(&mutex); N--; @@ -82,14 +102,25 @@ pthread_handler_t test_atomic_swap_handler(void *arg) */ pthread_handler_t test_atomic_cas_handler(void *arg) { - int m=*(int *)arg; - int32 x; + int m=*(int *)arg, ok; + int32 x,y; for (x=((int)(&m)); m ; m--) { - uint32 y=my_atomic_load32(&a32, &rwl); + my_atomic_rwlock_wrlock(&rwl); + y=my_atomic_load32(&a32); + my_atomic_rwlock_wrunlock(&rwl); + x=x*m+0x87654321; - while (!my_atomic_cas32(&a32, &y, y+x, &rwl)) ; - while (!my_atomic_cas32(&a32, &y, y-x, &rwl)) ; + do { + my_atomic_rwlock_wrlock(&rwl); + ok=my_atomic_cas32(&a32, &y, y+x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok); + do { + my_atomic_rwlock_wrlock(&rwl); + ok=my_atomic_cas32(&a32, &y, y-x); + my_atomic_rwlock_wrunlock(&rwl); + } while (!ok); } pthread_mutex_lock(&mutex); N--; @@ -103,9 +134,9 @@ void test_atomic(const char *test, pthread_handler handler, int n, int m) pthread_t t; ulonglong now=my_getsystime(); - my_atomic_store32(&a32, 0, &rwl); - my_atomic_store32(&b32, 0, &rwl); - my_atomic_store32(&c32, 0, &rwl); + a32= 0; + b32= 0; + c32= 0; diag("Testing %s with %d threads, %d iterations... ", test, n, m); for (N=n ; n ; n--) @@ -116,8 +147,7 @@ void test_atomic(const char *test, pthread_handler handler, int n, int m) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); now=my_getsystime()-now; - ok(my_atomic_load32(&a32, &rwl) == 0, - "tested %s in %g secs", test, ((double)now)/1e7); + ok(a32 == 0, "tested %s in %g secs", test, ((double)now)/1e7); } int main() diff --git a/unittest/mytap/tap.c b/unittest/mytap/tap.c index 2b728670cea..d3f5013b4c9 100644 --- a/unittest/mytap/tap.c +++ b/unittest/mytap/tap.c @@ -199,8 +199,8 @@ int exit_status() { if (g_test.plan != g_test.last) { - diag("%d tests planned but only %d executed", - g_test.plan, g_test.last); + diag("%d tests planned but%s %d executed", + g_test.plan, (g_test.plan > g_test.last ? " only" : ""), g_test.last); return EXIT_FAILURE; } diff --git a/win/build-vs71.bat b/win/build-vs71.bat index 46f3bc32f6e..959067695c5 100644 --- a/win/build-vs71.bat +++ b/win/build-vs71.bat @@ -1,6 +1,6 @@ @echo off -del cmakecache.txt +if exist cmakecache.txt del cmakecache.txt copy win\vs71cache.txt cmakecache.txt cmake -G "Visual Studio 7 .NET 2003" copy cmakecache.txt win\vs71cache.txt diff --git a/win/build-vs8.bat b/win/build-vs8.bat index 349c3e78628..d9c06241a9b 100644 --- a/win/build-vs8.bat +++ b/win/build-vs8.bat @@ -1,6 +1,6 @@ @echo off -del cmakecache.txt +if exist cmakecache.txt del cmakecache.txt copy win\vs8cache.txt cmakecache.txt cmake -G "Visual Studio 8 2005" copy cmakecache.txt win\vs8cache.txt