diff --git a/Docs/manual.texi b/Docs/manual.texi index f446b0ff925..e979c443763 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -11282,7 +11282,7 @@ mysql> SELECT name, birth, death, +--------+------------+------------+------+ @end example -The query uses @code{death IS NOT NULL} rather than @code{death != NULL} +The query uses @code{death IS NOT NULL} rather than @code{death <> NULL} because @code{NULL} is a special value. This is explained later. @xref{Working with NULL, , Working with @code{NULL}}. @@ -11367,12 +11367,12 @@ The @code{NULL} value can be surprising until you get used to it. Conceptually, @code{NULL} means missing value or unknown value and it is treated somewhat differently than other values. To test for @code{NULL}, you cannot use the arithmetic comparison operators such as @code{=}, @code{<}, -or @code{!=}. To demonstrate this for yourself, try the following query: +or @code{<>}. To demonstrate this for yourself, try the following query: @example -mysql> SELECT 1 = NULL, 1 != NULL, 1 < NULL, 1 > NULL; +mysql> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL; +----------+-----------+----------+----------+ -| 1 = NULL | 1 != NULL | 1 < NULL | 1 > NULL | +| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL | +----------+-----------+----------+----------+ | NULL | NULL | NULL | NULL | +----------+-----------+----------+----------+ @@ -11395,7 +11395,7 @@ The default truth value from a boolean operation is 1. This special treatment of @code{NULL} is why, in the previous section, it was necessary to determine which animals are no longer alive using -@code{death IS NOT NULL} instead of @code{death != NULL}. +@code{death IS NOT NULL} instead of @code{death <> NULL}. @node Pattern matching, Counting rows, Working with NULL, Retrieving data @@ -11413,7 +11413,7 @@ SQL pattern matching allows you to use @samp{_} to match any single character and @samp{%} to match an arbitrary number of characters (including zero characters). In MySQL, SQL patterns are case insensitive by default. Some examples are shown below. Note that you do not use @code{=} -or @code{!=} when you use SQL patterns; use the @code{LIKE} or @code{NOT +or @code{<>} when you use SQL patterns; use the @code{LIKE} or @code{NOT LIKE} comparison operators instead. To find names beginning with @samp{b}: @@ -46285,6 +46285,8 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. @itemize @bullet @item +Fixed problem with @code{GRANT} when using @code{lower_case_table_names == 1}. +@item Changed @code{SELECT ... IN SHARE MODE} to @code{SELECT .. LOCK IN SHARE MODE} (as in MySQL 3.23). @item diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 3a1fed143d1..4a7cb555dba 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -46,7 +46,7 @@ which () sleep_until_file_deleted () { file=$1 - loop=$SLEEP_TIME + loop=$SLEEP_TIME_FOR_DELETE while (test $loop -gt 0) do sleep 1 @@ -61,7 +61,8 @@ sleep_until_file_deleted () sleep_until_file_exists () { file=$1 - loop=60 # Should be long enough enough for all cases + loop=$2 + org_time=$2 while (test $loop -gt 0) do sleep 1 @@ -71,7 +72,7 @@ sleep_until_file_exists () fi loop=`expr $loop - 1` done - echo "ERROR: $file was not created in 60 seconds; Aborting" + echo "ERROR: $file was not created in $org_time seconds; Aborting" exit 1; } @@ -172,7 +173,11 @@ DO_GCOV="" DO_GDB="" DO_DDD="" DO_CLIENT_GDB="" -SLEEP_TIME=10 +SLEEP_TIME_FOR_DELETE=10 +SLEEP_TIME_FOR_FIRST_MASTER=200 # Enough time to create innodb tables +SLEEP_TIME_FOR_SECOND_MASTER=30 +SLEEP_TIME_FOR_FIRST_SLAVE=30 +SLEEP_TIME_FOR_SECOND_SLAVE=30 CHARACTER_SET=latin1 DBUSER="" START_WAIT_TIMEOUT=3 @@ -235,7 +240,6 @@ while test $# -gt 0; do EXTRA_MYSQL_TEST_OPT="$EXTRA_MYSQL_TEST_OPT $1" ;; --sleep=*) EXTRA_MYSQL_TEST_OPT="$EXTRA_MYSQL_TEST_OPT $1" - SLEEP_TIME=`$ECHO "$1" | $SED -e "s;--sleep=;;"` ;; --mysqld=*) TMP=`$ECHO "$1" | $SED -e "s;--mysqld=;;"` @@ -260,8 +264,9 @@ while test $# -gt 0; do fi DO_GDB=1 # We must use manager, as things doesn't work on Linux without it - USE_MANAGER=1 - USE_RUNNING_SERVER="" + # This needs to be checked properly + # USE_MANAGER=1 + # USE_RUNNING_SERVER="" ;; --client-gdb ) if [ x$BINARY_DIST = x1 ] ; then @@ -755,7 +760,8 @@ EOF else manager_launch master $MYSQLD $master_args fi - sleep_until_file_exists $MASTER_MYPID + sleep_until_file_exists $MASTER_MYPID $wait_for_master + wait_for_master=$SLEEP_TIME_FOR_SECOND_MASTER MASTER_RUNNING=1 } @@ -847,7 +853,8 @@ start_slave() manager_launch $slave_ident $SLAVE_MYSQLD $slave_args fi eval "SLAVE$1_RUNNING=1" - sleep_until_file_exists $slave_pid + sleep_until_file_exists $slave_pid $wait_for_slave + wait_for_slave=$SLEEP_TIME_FOR_SECOND_SLAVE } mysql_start () @@ -1143,6 +1150,8 @@ then # Remove files that can cause problems $RM -f $MYSQL_TEST_DIR/var/run/* $MYSQL_TEST_DIR/var/tmp/* + wait_for_master=$SLEEP_TIME_FOR_FIRST_MASTER + wait_for_slave=$SLEEP_TIME_FOR_FIRST_SLAVE $ECHO "Installing Test Databases" mysql_install_db start_manager diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index d0b4ab4efff..a0e234aa69c 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -216,3 +216,50 @@ select 1+1,"a",count(*) from t1 where foo in (2); 1+1 a count(*) 2 a 0 drop table t1; +CREATE TABLE t1 ( +spID int(10) unsigned, +userID int(10) unsigned, +score smallint(5) unsigned, +key (spid), +key (score) +); +INSERT INTO t1 VALUES (1,1,1),(2,2,2),(2,1,1),(3,3,3),(4,3,3),(5,3,3); +explain select userid,count(*) from t1 group by userid desc; +table type possible_keys key key_len ref rows Extra +t1 ALL NULL NULL NULL NULL 6 Using temporary +select userid,count(*) from t1 group by userid desc; +userid count(*) +3 3 +2 1 +1 2 +explain select spid,count(*) from t1 where spid between 1 and 2 group by spid desc; +table type possible_keys key key_len ref rows Extra +t1 range spID spID 5 NULL 2 where used; Using index +explain select spid,count(*) from t1 where spid between 1 and 2 group by spid; +table type possible_keys key key_len ref rows Extra +t1 range spID spID 5 NULL 2 where used; Using index +select spid,count(*) from t1 where spid between 1 and 2 group by spid; +spid count(*) +1 1 +2 2 +select spid,count(*) from t1 where spid between 1 and 2 group by spid desc; +spid count(*) +explain select sql_big_result spid,sum(userid) from t1 group by spid desc; +table type possible_keys key key_len ref rows Extra +t1 ALL NULL NULL NULL NULL 6 Using filesort +select sql_big_result spid,sum(userid) from t1 group by spid desc; +spid sum(userid) +5 3 +4 3 +3 3 +2 3 +1 1 +explain select sql_big_result score,count(*) from t1 group by score desc; +table type possible_keys key key_len ref rows Extra +t1 index NULL score 3 NULL 6 Using index +select sql_big_result score,count(*) from t1 group by score desc; +score count(*) +3 3 +2 1 +1 2 +drop table t1; diff --git a/mysql-test/r/innodb_cache.result b/mysql-test/r/innodb_cache.result new file mode 100644 index 00000000000..df331addb99 --- /dev/null +++ b/mysql-test/r/innodb_cache.result @@ -0,0 +1,15 @@ +drop table if exists t1; +set autocommit=0; +create table t1 (a int not null) type=innodb; +insert into t1 values (1),(2),(3); +select * from t1; +a +1 +2 +3 +show status like "Qcache_queries_in_cache"; +Variable_name Value +Qcache_queries_in_cache 0 +drop table t1; +commit; +set autocommit=1; diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index e6ba5fa83de..973d693d9aa 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1,3 +1,5 @@ +flush query cache; +flush query cache; reset query cache; flush status; drop table if exists t1,t2,t3; @@ -148,20 +150,6 @@ show status like "Qcache_queries_in_cache"; Variable_name Value Qcache_queries_in_cache 0 drop table t1, t2, t3; -set autocommit=0; -create table t1 (a int not null) type=innodb; -insert into t1 values (1),(2),(3); -select * from t1; -a -1 -2 -3 -show status like "Qcache_queries_in_cache"; -Variable_name Value -Qcache_queries_in_cache 0 -drop table t1; -commit; -set autocommit=1; create table t1 (a int not null); insert into t1 values (1),(2),(3); create table t2 (a int not null); diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index f2572321d61..6a27df648b5 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -74,6 +74,14 @@ a b 2 b 3 c 4 d +(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1); +a b +1 a +(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by b desc; +a b +3 c +2 b +1 a explain select a,b from t1 union all select a,b from t2; table type possible_keys key key_len ref rows Extra t1 ALL NULL NULL NULL NULL 4 diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index 49f5c6143f3..27d7f2e68d5 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -35,6 +35,7 @@ select insert('txs',2,1,'hi'),insert('is ',4,0,'a'),insert('txxxxt',2,4,'es'); select replace('aaaa','a','b'),replace('aaaa','aa','b'),replace('aaaa','a','bb'),replace('aaaa','','b'),replace('bbbb','a','c'); select replace(concat(lcase(concat('THIS',' ','IS',' ','A',' ')),ucase('false'),' ','test'),'FALSE','REAL') ; select soundex(''),soundex('he'),soundex('hello all folks'); +--replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l. select password('test'),length(encrypt('test')),encrypt('test','aa'); select md5('hello'); select repeat('monty',5),concat('*',space(5),'*'); diff --git a/mysql-test/t/innodb_cache-master.opt b/mysql-test/t/innodb_cache-master.opt new file mode 100644 index 00000000000..5f0ebff98f6 --- /dev/null +++ b/mysql-test/t/innodb_cache-master.opt @@ -0,0 +1 @@ +--set-variable=query_cache_size=1M diff --git a/mysql-test/t/innodb_cache.test b/mysql-test/t/innodb_cache.test new file mode 100644 index 00000000000..0a162a9debe --- /dev/null +++ b/mysql-test/t/innodb_cache.test @@ -0,0 +1,14 @@ +-- source include/have_innodb.inc + +# +# Without auto_commit. +# +drop table if exists t1; +set autocommit=0; +create table t1 (a int not null) type=innodb; +insert into t1 values (1),(2),(3); +select * from t1; +show status like "Qcache_queries_in_cache"; +drop table t1; +commit; +set autocommit=1; diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 59ea6d8777b..d2af702e566 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1,11 +1,11 @@ --- source include/have_innodb.inc - # # Tests with query cache # # Reset query cache variables. +flush query cache; # This crashed in some versions +flush query cache; # This crashed in some versions reset query cache; flush status; drop table if exists t1,t2,t3; @@ -73,17 +73,6 @@ delete from t3 where a=10; show status like "Qcache_queries_in_cache"; drop table t1, t2, t3; # -# Without auto_commit. -# -set autocommit=0; -create table t1 (a int not null) type=innodb; -insert into t1 values (1),(2),(3); -select * from t1; -show status like "Qcache_queries_in_cache"; -drop table t1; -commit; -set autocommit=1; -# # FLUSH QUERY CACHE # create table t1 (a int not null); diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 0d391f6f37b..8d98e6e0389 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -18,6 +18,8 @@ select 't1',b,count(*) from t1 group by b UNION select 't2',b,count(*) from t2 g #test alternate syntax for unions (select a,b from t1 limit 2) union all (select a,b from t2 order by a) limit 4; +(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1); +(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by b desc; # Test some error conditions with UNION explain select a,b from t1 union all select a,b from t2; diff --git a/mysys/my_winsem.c b/mysys/my_winsem.c index d45b43a4473..268a05a0b21 100644 --- a/mysys/my_winsem.c +++ b/mysys/my_winsem.c @@ -4,10 +4,10 @@ * Module: my_semaphore.c (Original: semaphore.c from pthreads library) * * Purpose: - * Semaphores aren't actually part of the PThreads standard. - * They are defined by the POSIX Standard: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: * - * POSIX 1003.1b-1993 (POSIX.1b) + * POSIX 1003.1b-1993 (POSIX.1b) * * ------------------------------------------------------------- * @@ -52,33 +52,27 @@ DOCPUBLIC This function initializes an unnamed semaphore. the initial value of the semaphore is 'value' - + PARAMETERS - sem - pointer to an instance of sem_t - - pshared - if zero, this semaphore may only be shared between - threads in the same process. - if nonzero, the semaphore can be shared between - processes - - value - initial value of the semaphore counter - - DESCRIPTION - This function initializes an unnamed semaphore. The - initial value of the semaphore is set to 'value'. - + sem Pointer to an instance of sem_t + + pshared If zero, this semaphore may only be shared between + threads in the same process. + If nonzero, the semaphore can be shared between + processes + + value Initial value of the semaphore counter + RESULTS - 0 successfully created semaphore, - -1 failed, error in errno + 0 Successfully created semaphore, + -1 Failed, error in errno + ERRNO - EINVAL 'sem' is not a valid semaphore, - ENOSPC a required resource has been exhausted, - ENOSYS semaphores are not supported, - EPERM the process lacks appropriate privilege - + EINVAL 'sem' is not a valid semaphore, + ENOSPC A required resource has been exhausted, + ENOSYS Semaphores are not supported, + EPERM The process lacks appropriate privilege + */ int @@ -111,10 +105,10 @@ sem_init (sem_t *sem, int pshared, unsigned int value) InitializeCriticalSection(&sem->sem_lock_cs); } #else /* HAVE_CREATESEMAPHORE */ - *sem = CreateSemaphore (NULL, /* Always NULL */ - value, /* Initial value */ + *sem = CreateSemaphore (NULL, /* Always NULL */ + value, /* Initial value */ 0x7FFFFFFFL, /* Maximum value */ - NULL); /* Name */ + NULL); /* Name */ if (!*sem) result = ENOSPC; #endif /* HAVE_CREATESEMAPHORE */ @@ -133,20 +127,15 @@ sem_init (sem_t *sem, int pshared, unsigned int value) This function destroys an unnamed semaphore. PARAMETERS - sem - pointer to an instance of sem_t - - DESCRIPTION - This function destroys an unnamed semaphore. + sem Pointer to an instance of sem_t RESULTS - 0 successfully destroyed semaphore, - -1 failed, error in errno + 0 Successfully destroyed semaphore, + -1 Failed, error in errno ERRNO - EINVAL 'sem' is not a valid semaphore, - ENOSYS semaphores are not supported, - EBUSY threads (or processes) are currently - blocked on 'sem' + EINVAL 'sem' is not a valid semaphore, + ENOSYS Semaphores are not supported, + EBUSY Threads (or processes) are currently blocked on 'sem' */ int @@ -154,7 +143,7 @@ sem_destroy (sem_t * sem) { int result = 0; -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG if (sem == NULL || *sem == NULL) { errno=EINVAL; @@ -183,27 +172,24 @@ sem_destroy (sem_t * sem) /* DOCPUBLIC - This function tries to wait on a semaphore. - - PARAMETERS - sem - pointer to an instance of sem_t - - DESCRIPTION This function tries to wait on a semaphore. If the semaphore value is greater than zero, it decreases its value by one. If the semaphore value is zero, then this function returns immediately with the error EAGAIN + PARAMETERS + sem Pointer to an instance of sem_t + RESULTS - 0 successfully decreased semaphore, - -1 failed, error in errno + 0 Successfully decreased semaphore, + -1 Failed, error in errno + ERRNO - EAGAIN the semaphore was already locked, - EINVAL 'sem' is not a valid semaphore, - ENOSYS semaphores are not supported, - EINTR the function was interrupted by a signal, - EDEADLK a deadlock condition was detected. + EAGAIN The semaphore was already locked, + EINVAL 'sem' is not a valid semaphore, + ENOSYS Semaphores are not supported, + EINTR The function was interrupted by a signal, + EDEADLK A deadlock condition was detected. */ int @@ -214,7 +200,7 @@ sem_trywait(sem_t * sem) int errno = EINVAL; return -1; #else /* HAVE_CREATESEMAPHORE */ -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG if (sem == NULL || *sem == NULL) { errno=EINVAL; @@ -234,7 +220,7 @@ sem_trywait(sem_t * sem) #ifndef HAVE_CREATESEMAPHORE -static void +static void ptw32_decrease_semaphore(sem_t * sem) { EnterCriticalSection(&sem->sem_lock_cs); @@ -267,13 +253,6 @@ ptw32_increase_semaphore(sem_t * sem, unsigned int n) /* ------------------------------------------------------ DOCPUBLIC - This function waits on a semaphore. - - PARAMETERS - sem - pointer to an instance of sem_t - - DESCRIPTION This function waits on a semaphore. If the semaphore value is greater than zero, it decreases its value by one. If the semaphore value is zero, then @@ -281,15 +260,18 @@ ptw32_increase_semaphore(sem_t * sem, unsigned int n) successfully decrease the value or until interrupted by a signal. - RESULTS - 0 successfully decreased semaphore, - -1 failed, error in errno - ERRNO - EINVAL 'sem' is not a valid semaphore, - ENOSYS semaphores are not supported, - EINTR the function was interrupted by a signal, - EDEADLK a deadlock condition was detected. + PARAMETERS + sem Pointer to an instance of sem_t + RESULTS + 0 Successfully decreased semaphore, + -1 Failed, error in errno + + ERRNO + EINVAL 'Sem' is not a valid semaphore, + ENOSYS Semaphores are not supported, + EINTR The function was interrupted by a signal, + EDEADLK A deadlock condition was detected. */ int @@ -297,7 +279,7 @@ sem_wait(sem_t *sem) { int result; -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG if (sem == NULL || *sem == NULL) { errno=EINVAL; @@ -331,30 +313,27 @@ sem_wait(sem_t *sem) /* ------------------------------------------------------ DOCPUBLIC - This function posts a wakeup to a semaphore. - - PARAMETERS - sem - pointer to an instance of sem_t - - DESCRIPTION This function posts a wakeup to a semaphore. If there are waiting threads (or processes), one is awakened; otherwise, the semaphore value is incremented by one. + PARAMETERS + sem Pointer to an instance of sem_t + RESULTS - 0 successfully posted semaphore, - -1 failed, error in errno + 0 Successfully posted semaphore, + -1 Failed, error in errno + ERRNO - EINVAL 'sem' is not a valid semaphore, - ENOSYS semaphores are not supported, + EINVAL 'sem' is not a valid semaphore, + ENOSYS Semaphores are not supported, */ int sem_post (sem_t * sem) { -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG if (sem == NULL || *sem == NULL) { errno=EINVAL; @@ -378,32 +357,27 @@ sem_post (sem_t * sem) /* ------------------------------------------------------ DOCPUBLIC - This function posts multiple wakeups to a semaphore. - - PARAMETERS - sem - pointer to an instance of sem_t - - count - counter, must be greater than zero. - - DESCRIPTION This function posts multiple wakeups to a semaphore. If there are waiting threads (or processes), n <= count are awakened; the semaphore value is incremented by count - n. + PARAMETERS + sem Pointer to an instance of sem_t + count Counter, must be greater than zero. + RESULTS - 0 successfully posted semaphore, - -1 failed, error in errno + 0 Successfully posted semaphore, + -1 Failed, error in errno + ERRNO - EINVAL 'sem' is not a valid semaphore - or count is less than or equal to zero. + EINVAL 'sem' is not a valid semaphore or count is less + than or equal to zero. */ int sem_post_multiple (sem_t * sem, int count ) { -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG if (sem == NULL || *sem == NULL || count <= 0) { errno=EINVAL; diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc index 253a8ea7516..164c2ab706e 100644 --- a/sql/des_key_file.cc +++ b/sql/des_key_file.cc @@ -15,72 +15,80 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #ifdef HAVE_OPENSSL + /* - Function which loads DES keys from plaintext file - into memory on MySQL server startup and on command - FLUSH DES_KEYS. Blame tonu@spam.ee on bugs ;) + Function which loads DES keys from plaintext file into memory on MySQL + server startup and on command FLUSH DES_KEYS. Blame tonu@spam.ee on bugs ;) */ -void + +struct st_des_keyschedule des_keyschedule[10]; +uint default_des_key; + +void load_des_key_file(const char *file_name) { - FILE *file; - int ret=0; - char offset; - char buf[1024]; + File file; des_cblock ivec={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - st_des_keyblock keyblock; + char offset; + IO_CACHE io; DBUG_ENTER("load_des_key_file"); - VOID(pthread_mutex_lock(&LOCK_open)); DBUG_PRINT("enter",("name: %s",file_name)); - if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME)))) + + VOID(pthread_mutex_lock(&LOCK_open)); + if ((file=my_open(file_name,O_RDONLY | O_BINARY ,MYF(MY_WME))) < 0 || + init_io_cache(&io, file, IO_SIZE*2, READ_CACHE, 0, 0, MYF(MY_WME))) + goto error; + + bzero((char*) des_keyschedule,sizeof(struct st_des_keyschedule) * 10); + default_des_key=15; // Impossible key + for (;;) { - goto error_noclose; - } - while(!feof(file)) - { - if ((my_fread(file, &offset, 1, MY_WME)) != 1) - goto error_close; - fgets(buf,sizeof(buf),file); - int len=strlen(buf); - if (len-->=1) - buf[len]='\0'; - /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ - offset-='0'; - if (offset >= 0 && offset <=9) + char *start, *end; + char buf[1024]; + st_des_keyblock keyblock; + uint length; + + if (!(length=my_b_gets(&io,buf,sizeof(buf)-1))) + break; // End of file + offset=buf[0]; + if (offset >= '0' && offset <= '9') // If ok key { - EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, - (uchar *)buf, - strlen(buf),1,(uchar *)&keyblock,ivec); - des_set_key_unchecked(&keyblock.key1,des_keyschedule[(int)offset].ks1); - des_set_key_unchecked(&keyblock.key2,des_keyschedule[(int)offset].ks2); - des_set_key_unchecked(&keyblock.key3,des_keyschedule[(int)offset].ks3); - } + offset=(char) (offset - '0'); + // Remove newline and possible other control characters + for (start=buf+1 ; isspace(*start) ; start++) ; + end=buf+length; + for (end=strend(buf) ; end > start && iscntrl(end[-1]) ; end--) ; + + if (start != end) + { + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar *) start, (int) (end-start),1, + (uchar *) &keyblock, + ivec); + des_set_key_unchecked(&keyblock.key1,des_keyschedule[(int)offset].ks1); + des_set_key_unchecked(&keyblock.key2,des_keyschedule[(int)offset].ks2); + des_set_key_unchecked(&keyblock.key3,des_keyschedule[(int)offset].ks3); + if (default_des_key == 15) + default_des_key= (uint) offset; // use first as def. + } + } else { - DBUG_PRINT("des",("wrong offset: %d",offset)); + DBUG_PRINT("des",("wrong offset: %c",offset)); } } -error_close: - (void) my_fclose(file,MYF(MY_WME)); -error_noclose: + +error: + if (file >= 0) + { + my_close(file,MYF(0)); + end_io_cache(&io); + } VOID(pthread_mutex_unlock(&LOCK_open)); - /* if (ret) - do something; */ DBUG_VOID_RETURN; } - -/* - This function is used to load right key with DES_ENCRYPT(text,integer) -*/ -st_des_keyschedule * -des_key(int key) -{ - DBUG_ENTER("des_key"); - DBUG_PRINT("exit",("return: %x",&des_keyschedule[key])); - DBUG_RETURN(&des_keyschedule[key]); -} - #endif /* HAVE_OPENSSL */ - diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ddecc62b749..004acd2d5ec 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -75,7 +75,11 @@ String *Item_func_md5::val_str(String *str) my_MD5Init (&context); my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); my_MD5Final (digest, &context); - str->alloc(32); // Ensure that memory is free + if (str->alloc(32)) // Ensure that memory is free + { + null_value=1; + return 0; + } sprintf((char *) str->ptr(), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], @@ -201,162 +205,150 @@ void Item_func_concat::fix_length_and_dec() } } -#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') -#define ascii_to_bin(c) ((c)<=57 ? (c)-46 : (c)<=90 ? (c)-53 : (c)-59) - /* - Function des_encrypt() by tonu@spam.ee - Works only if compiled with OpenSSL library support. - Output always starts with magic char "1" and all - encrypted output is encoded into ASCII-protected - container. - Original input is returned as output if input string - begins with magic "1". Credit card number always begin - with 4,5 or 6. + Function des_encrypt() by tonu@spam.ee & monty + Works only if compiled with OpenSSL library support. + This returns a binary string where first character is + CHAR(128 | tail-length << 4 | key-number). + If one uses a string key key_number is 0. Encryption result is longer than original by formula: - new_length=(8-(original_length % 8))*2+1 + new_length= (8-(original_length % 8))+1 */ String *Item_func_des_encrypt::val_str(String *str) { - String *res =args[0]->val_str(str); #ifdef HAVE_OPENSSL des_cblock ivec={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; struct st_des_keyblock keyblock; struct st_des_keyschedule keyschedule; struct st_des_keyschedule *keyschedule_ptr=&keyschedule; + uint key_number=15; + String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) return &empty_string; - if(res->c_ptr()[0]!='1') // Skip encryption if already encrypted - { - if (args[1]->val_int()) - { - keyschedule_ptr=des_key(args[1]->val_int()); - } - else - { - String *keystr=args[1]->val_str(&tmp_value); - /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ - EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, - (uchar *)keystr->c_ptr(), - (int)keystr->length(),1,(uchar *)&keyblock,ivec); - des_set_key_unchecked(&keyblock.key1,keyschedule_ptr->ks1); - des_set_key_unchecked(&keyblock.key2,keyschedule_ptr->ks2); - des_set_key_unchecked(&keyblock.key3,keyschedule_ptr->ks3); - } - /* - The problem: DES algorithm requires original data to be in 8-bytes - chunks. Missing bytes get filled with zeros and result of encryption - can be up to 7 bytes longer than original string. When decrypted, - we do not know the size of original string :( - We add one byte with value 0x0..0x7 to original plaintext marking - change of string length - */ - uchar tail= 7-( res->length() %8); // 0..7 marking real offsets 1..8 - for(int i=0 ; i < tail ; ++i) res->append('*'); - res->append(tail); // Write tail length 0..7 to last pos - str->length(res->length()); - for (uint j=0; j < res->length() ; ++j) - { - DBUG_PRINT("info",("## res->c_ptr()[%d]='%c'",j,res->c_ptr()[j])); - } - des_ede3_cbc_encrypt( // Real encryption - (const uchar*)(res->c_ptr()), - (uchar*)(str->c_ptr()), - res->length(), - keyschedule_ptr->ks1, keyschedule_ptr->ks2, keyschedule_ptr->ks3, - &ivec, TRUE); - for (uint j=0; j < res->length() ; ++j) - { - DBUG_PRINT("info",("## str->c_ptr()[%d]='%c'",j,str->c_ptr()[j])); - } - res->set((const char*)"1",(uint)1); - for(uint i=0 ; i < str->length() ; ++i) - { - res->append(bin_to_ascii((uchar)str->c_ptr()[i] & 0x3f)); - res->append(bin_to_ascii(((uchar)str->c_ptr()[i] >> 5 ) & 0x3f)); - } + if (arg_count == 1) + keyschedule_ptr=des_keyschedule[key_number=default_des_key]; + else if (args[1]->result_type == INT_RESULT) + { + key_number= (uint) args[1]->val_int(); + if (key_number > 9) + goto error; + keyschedule_ptr= des_keyschedule[key_number]; } - return res; -#else + else + { + const char *append_str="********"; + uint tail,res_length; + String *keystr=args[1]->val_str(&tmp_value); + if (!keystr) + goto error; + + /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(), (int) keystr->length(), + 1, (uchar*) &keyblock,ivec); + des_set_key_unchecked(&keyblock.key1,keyschedule_ptr->ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule_ptr->ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule_ptr->ks3); + } + + /* + The problem: DES algorithm requires original data to be in 8-bytes + chunks. Missing bytes get filled with zeros and result of encryption + can be up to 7 bytes longer than original string. When decrypted, + we do not know the size of original string :( + We add one byte with value 0x1..0x8 as the second byte to original + plaintext marking change of string length. + */ + + tail= (7-(res->length()+7) % 8); // 0..7 marking extra length + res_length=res->length()+tail+1; + if (tail && res->append(append_str, tail) || tmp_value.alloc(res_length)) + goto err; + + tmp_value.length(res_length); + tmp_value.[0]=(char) (128 | tail << 4 | key_number); + // Real encryption + des_ede3_cbc_encrypt((const uchar*) (res->ptr()), + (uchar*) (tmp_value->ptr()+1), + res->length(), + keyschedule_ptr->ks1, + keyschedule_ptr->ks2, + keyschedule_ptr->ks3, + &ivec, TRUE); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ null_value=1; return 0; -#endif /* HAVE_OPENSSL */ } + String *Item_func_des_decrypt::val_str(String *str) { - String *res =args[0]->val_str(str); #ifdef HAVE_OPENSSL des_key_schedule ks1, ks2, ks3; des_cblock ivec={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; struct st_des_keyblock keyblock; struct st_des_keyschedule keyschedule; struct st_des_keyschedule *keyschedule_ptr=&keyschedule; + String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; - if (res->length() == 0) - return &empty_string; + if (res->length(0) < 9 || (res->length()) % 8 != 1 || !(res->[0] & 128)) + return res; // Skip decryption if not encrypted - if(res->c_ptr()[0]=='1') // Skip decryption if not encrypted + if (arg_count == 1) // If automatic uncompression { - str->set((const char*)0,(uint)0); - for(uint i=1 ; i < res->length() ; i+=2) - { - str->append((ascii_to_bin(res->c_ptr()[i])) - | (ascii_to_bin(res->c_ptr()[i+1]) << 5 )); - } - - if (args[1]->val_int()) - { - keyschedule_ptr=des_key(args[1]->val_int()); - } - else - { - /* - We make good 24-byte (168 bit) key - from given plaintext key with MD5 - */ - String *keystr=args[1]->val_str(&tmp_value); - EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, - (uchar *)keystr->c_ptr(), - (int)keystr->length(),1,(uchar *)&keyblock,ivec); - /* - Here we set all 64-bit keys (56 effective) one by one - */ - des_set_key_unchecked(&keyblock.key1,keyschedule_ptr->ks1); - des_set_key_unchecked(&keyblock.key2,keyschedule_ptr->ks2); - des_set_key_unchecked(&keyblock.key3,keyschedule_ptr->ks3); - } - res->length(str->length()); - - des_ede3_cbc_encrypt( // Real decryption - (const uchar*)(str->c_ptr()), - (uchar*)(res->c_ptr()), - str->length(), - keyschedule_ptr->ks1, keyschedule_ptr->ks2, keyschedule_ptr->ks3, - &ivec, FALSE); - uchar tail=(res->c_ptr()[res->length()-1]) & 0x7; - if ((res->length() > ((uint)1+tail))) // We should avoid negative length - res->length(res->length()-1-tail); // (can happen with wrong key) + uint key_number=res->[0] & 15; + // Check if automatic key and that we have privilege to uncompress using it + if (!(current_thd->master_access & PROCESS_ACL) || key_number > 9) + goto error; + keyschedule_ptr=des_keyschedule[key_number-1]; } - return res; -#else + else + { + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + String *keystr=args[1]->val_str(&tmp_value); + if (!key_str) + goto error; + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(),(int) keystr->length(), + 1,(uchar*) &keyblock,ivec); + // Here we set all 64-bit keys (56 effective) one by one + des_set_key_unchecked(&keyblock.key1,keyschedule_ptr->ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule_ptr->ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule_ptr->ks3); + } + if (tmp_value.alloc(res->length()-1)) + goto err; + /* Restore old length of key */ + tmp_value.length(res->length()-1-(((uchar) res->[0] >> 4) & 7)); + des_ede3_cbc_encrypt((const uchar*) res->ptr()+1, + (uchar*) (tmp_value->ptr()), + res->length()-1, + keyschedule_ptr->ks1, + keyschedule_ptr->ks2, + keyschedule_ptr->ks3, + &ivec, FALSE); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ null_value=1; return 0; -#endif /* HAVE_OPENSSL */ } - /* -** concat with separator. First arg is the separator -** concat_ws takes at least two arguments. + concat with separator. First arg is the separator + concat_ws takes at least two arguments. */ String *Item_func_concat_ws::val_str(String *str) @@ -1146,6 +1138,7 @@ String *Item_func_password::val_str(String *str) return str; } +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') String *Item_func_encrypt::val_str(String *str) { diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 31c832c8ddb..ba2d6ffc3f0 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -228,9 +228,9 @@ class Item_func_des_encrypt :public Item_str_func public: Item_func_des_encrypt(Item *a) :Item_str_func(a) {} Item_func_des_encrypt(Item *a, Item *b): Item_str_func(a,b) {} - Item_func_des_encrypt(Item *a, Item *b, Item *c): Item_str_func(a,b,c) {} String *val_str(String *); - void fix_length_and_dec() { maybe_null=1; max_length = args[0]->max_length; } + void fix_length_and_dec() + { maybe_null=1; max_length = args[0]->max_length+8; } const char *func_name() const { return "des_encrypt"; } }; @@ -240,7 +240,6 @@ class Item_func_des_decrypt :public Item_str_func public: Item_func_des_decrypt(Item *a) :Item_str_func(a) {} Item_func_des_decrypt(Item *a, Item *b): Item_str_func(a,b) {} - Item_func_des_decrypt(Item *a, Item *b, Item *c): Item_str_func(a,b,c) {} String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length = args[0]->max_length; } const char *func_name() const { return "des_decrypt"; } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 20f7b8c3d7b..dad5f27a3e0 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -405,9 +405,9 @@ struct st_des_keyschedule { des_key_schedule ks1, ks2, ks3; }; -extern struct st_des_keyschedule des_keyschedule[10]; +extern struct st_des_keyschedule des_keyschedule[9]; +extern uint des_default_key; void load_des_key_file(const char *file_name); -struct st_des_keyschedule * des_key(int); #endif /* HAVE_OPENSSL */ /* sql_list.c */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9bbdc7b8859..a8b9a799c56 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -243,7 +243,6 @@ static char glob_hostname[FN_REFLEN]; #include "sslopt-vars.h" #ifdef HAVE_OPENSSL static char * des_key_file = 0; -struct st_des_keyschedule des_keyschedule[10]; struct st_VioSSLAcceptorFd * ssl_acceptor_fd = 0; #endif /* HAVE_OPENSSL */ @@ -1751,8 +1750,6 @@ int main(int argc, char **argv) opt_use_ssl = 0; /* having ssl_acceptor_fd != 0 signals the use of SSL */ } - bzero(des_keyschedule,sizeof(struct st_des_keyschedule) * 10); - DBUG_PRINT("des",("initializing %d bytes of %x",sizeof(struct st_des_keyschedule) * 10, des_keyschedule)); if (des_key_file) load_des_key_file(des_key_file); #endif /* HAVE_OPENSSL */ diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6a54e3b2b16..0d2568e8c5e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -698,12 +698,17 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip, { uint host_access,db_access,i,key_length; db_access=0; host_access= ~0; - char key[ACL_KEY_LENGTH],*end; + char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; VOID(pthread_mutex_lock(&acl_cache->lock)); memcpy_fixed(&key,bin_ip,sizeof(struct in_addr)); - end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db); + end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db); + if (lower_case_table_names) + { + casedn_str(tmp_db); + db=tmp_db; + } key_length=(uint) (end-key); if ((entry=(acl_entry*) acl_cache->search(key,key_length))) { @@ -1377,6 +1382,11 @@ public: db = strdup_root(&memex,d); user = strdup_root(&memex,u); tname= strdup_root(&memex,t); + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); @@ -1398,7 +1408,13 @@ public: privs = cols = 0; /* purecov: inspected */ return; /* purecov: inspected */ } - key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3; + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } + key_length = ((uint) strlen(db) + (uint) strlen(user) + + (uint) strlen(tname) + 3); hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (uint) form->field[6]->val_int(); @@ -1990,7 +2006,7 @@ int mysql_grant (THD *thd, const char *db, List &list, uint rights, { List_iterator str_list (list); LEX_USER *Str; - char what; + char what,tmp_db[NAME_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; DBUG_ENTER("mysql_grant"); @@ -2002,6 +2018,12 @@ int mysql_grant (THD *thd, const char *db, List &list, uint rights, } what = (revoke_grant) ? 'N' : 'Y'; + if (lower_case_table_names && db) + { + strmov(tmp_db,db); + casedn_str(tmp_db); + db=tmp_db; + } /* open the mysql.user and mysql.db tables */ @@ -2220,8 +2242,8 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, table->grant.want_privilege=0; continue; // Already checked } - const char *db = table->db ? table->db : thd->db; - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user, + GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0); if (!grant_table) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 4417af44907..7f3bb97c7d6 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -201,7 +201,7 @@ stored in Query_cache_memory_bin_step structure. Free memory blocks are sorted in bins in lists with size-ascending order (more small blocks needed frequently then bigger one). -6. Packing cache. +7. Packing cache. Query cache packing is divided into two operation: - pack_cache @@ -305,7 +305,8 @@ If join_results allocated new block(s) then we need call pack_cache again. #define BLOCK_UNLOCK_RD(B) { \ DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\ __LINE__,(ulong)(B)));B->query()->unlock_reading();} -#define DUMP(C) DBUG_EXECUTE("qcache", {(C)->queries_dump();(C)->tables_dump();}) +#define DUMP(C) DBUG_EXECUTE("qcache", {\ + (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();}) #else #define MUTEX_LOCK(M) pthread_mutex_lock(M) #define MUTEX_UNLOCK(M) pthread_mutex_unlock(M) @@ -581,6 +582,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length) else STRUCT_UNLOCK(&query_cache.structure_guard_mutex); } + DBUG_EXECUTE("check_querycache",query_cache.check_integrity();); DBUG_VOID_RETURN; } @@ -609,6 +611,7 @@ void query_cache_abort(NET *net) } STRUCT_UNLOCK(&query_cache.structure_guard_mutex); } + DBUG_EXECUTE("check_querycache",query_cache.check_integrity();); DBUG_VOID_RETURN; } @@ -656,12 +659,14 @@ void query_cache_end_of_result(NET *net) } net->query_cache_query=0; } + DBUG_EXECUTE("check_querycache",query_cache.check_integrity();); DBUG_VOID_RETURN; } void query_cache_invalidate_by_MyISAM_filename(const char *filename) { query_cache.invalidate_by_MyISAM_filename(filename); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity();); } @@ -1321,6 +1326,7 @@ void Query_cache::free_cache(my_bool destruction) if (bins[0].free_blocks == 0) { wreck(__LINE__,"no free memory found in (bins[0].free_blocks"); + DBUG_VOID_RETURN; } #endif @@ -1335,12 +1341,13 @@ void Query_cache::free_cache(my_bool destruction) hash_free(&tables); if (!destruction) STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_EXECUTE("check_querycache",check_integrity();); } DBUG_VOID_RETURN; } /***************************************************************************** - Free block data + Free block data *****************************************************************************/ /* @@ -1501,7 +1508,6 @@ Query_cache::append_result_data(Query_cache_block **current_block, DBUG_PRINT("qcache", ("size limit reached %lu > %lu", query_block->query()->length(), query_cache_limit)); - *current_block=0; // Mark error DBUG_RETURN(0); } if (*current_block == 0) @@ -1550,8 +1556,8 @@ Query_cache::append_result_data(Query_cache_block **current_block, query_block, Query_cache_block::RES_CONT); /* - new_block may be not 0 even !success (if write_result_data - allocate small block but failed allocate continue + new_block may be != 0 even !success (if write_result_data + allocate a small block but failed to allocate continue) */ if (new_block != 0) double_linked_list_join(last_block, new_block); @@ -1999,7 +2005,7 @@ Query_cache::get_free_block(ulong len, my_bool not_less, ulong min) void Query_cache::free_memory_block(Query_cache_block *block) { - DBUG_ENTER("Query_cache::free_n_unlock_memory_block"); + DBUG_ENTER("Query_cache::free_memory_block"); block->used=0; DBUG_PRINT("qcache",("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx", (ulong) first_block, (ulong) block,block->pnext, @@ -2069,7 +2075,7 @@ my_bool Query_cache::append_next_free_block(Query_cache_block *block, DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block, add_size)); - if (next_block->is_free()) + if (next_block != first_block && next_block->is_free()) { ulong old_len = block->length; exclude_from_free_memory_list(next_block); @@ -2404,7 +2410,7 @@ my_bool Query_cache::move_by_type(byte **border, char *data = (char*) block->data(); byte *key; uint key_length; - key=query_cache_table_get_key((byte*) block, &key_length,0); + key=query_cache_table_get_key((byte*) block, &key_length, 0); hash_search(&tables, (byte*) key, key_length); block->destroy(); @@ -2452,7 +2458,7 @@ my_bool Query_cache::move_by_type(byte **border, block->data())->result(); byte *key; uint key_length; - key=query_cache_query_get_key((byte*) block, &key_length,0); + key=query_cache_query_get_key((byte*) block, &key_length, 0); hash_search(&queries, (byte*) key, key_length); memcpy((char*) new_block->table(0), (char*) block->table(0), @@ -2682,8 +2688,9 @@ void Query_cache::wreck(uint line, const char *message) DBUG_PRINT("warning", ("==================================")); if (thd) thd->killed = 1; - bins_dump(); cache_dump(); + /* check_integrity(); */ /* Can't call it here because of locks */ + bins_dump(); DBUG_VOID_RETURN; } @@ -2821,4 +2828,347 @@ void Query_cache::tables_dump() } DBUG_PRINT("qcache", ("--------------------")); } + + +my_bool Query_cache::check_integrity() +{ + my_bool result = 0; + uint i; + STRUCT_LOCK(&structure_guard_mutex); + + if (hash_check(&queries)) + { + DBUG_PRINT("error", ("queries hash is damaged")); + result = 1; + } + + if (hash_check(&tables)) + { + DBUG_PRINT("error", ("tables hash is damaged")); + result = 1; + } + + DBUG_PRINT("qcache", ("physical address check ...")); + ulong free=0, used=0; + Query_cache_block * block = first_block; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + // Check memory allocation + if (block->pnext == first_block) // Is it last block? + { + if ( ((byte*)block) + block->length != + ((byte*)first_block) + query_cache_size ) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + } + else + if (((byte*)block) + block->length != ((byte*)block->pnext)) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) ((byte*)block->pnext))); + } + if (block->type == Query_cache_block::FREE) + free+=block->length; + else + used+=block->length; + switch(block->type) { + case Query_cache_block::FREE: + { + Query_cache_memory_bin *bin = *((Query_cache_memory_bin **) + block->data()); + //is it correct pointer? + if ( ((byte*)bin) < ((byte*)bins) || + ((byte*)bin) >= ((byte*)first_block)) + { + DBUG_PRINT("error", + ("free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) bin, + (ulong) bins, + (ulong) first_block)); + result = 1; + } + else + { + int idx = (((byte*)bin) - ((byte*)bins)) / + sizeof(Query_cache_memory_bin); + if (in_list(bins[idx].free_blocks, block, "free memory")) + result = 1; + } + break; + } + case Query_cache_block::TABLE: + if (in_list(tables_blocks[block->table()->type()], block, "tables")) + result = 1; + break; + case Query_cache_block::QUERY: + if (in_list(queries_blocks, block, "query")) + result = 1; + break; + case Query_cache_block::RES_INCOMPLETE: + case Query_cache_block::RES_BEG: + case Query_cache_block::RES_CONT: + case Query_cache_block::RESULT: + { + Query_cache_block * query_block = block->result()->parent(); + if ( ((byte*)query_block) < ((byte*)first_block) || + ((byte*)query_block) >= (((byte*)first_block) + query_cache_size)) + { + DBUG_PRINT("error", + ("result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) query_block, + (ulong) first_block, + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + else + { + if (in_list(queries_blocks, query_block, "query from results")) + result = 1; + if (in_list(query_block->query()->result(), block, + "results")) + result = 1; + } + break; + } + default: + DBUG_PRINT("error", + ("block 0x%lx have incorrect type %u", + block, block->type)); + result = 1; + } + + block = block->pnext; + } while (block != first_block); + + if (used + free != query_cache_size) + { + DBUG_PRINT("error", + ("used memory (%lu) + free memory (%lu) != query_cache_size (%lu)", + used, free, query_cache_size)); + result = 1; + } + + if (free != free_memory) + { + DBUG_PRINT("error", + ("free memory (%lu) != free_memory (%lu)", + free, free_memory)); + result = 1; + } + + DBUG_PRINT("qcache", ("check queries ...")); + if ((block = queries_blocks)) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_query_get_key((byte*) block, &length, 0); + gptr val = hash_search(&queries, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx", + (ulong) block, (ulong) val)); + } + if (in_blocks(block)) + result = 1; + Query_cache_block * results = block->query()->result(); + if (results) + { + Query_cache_block * result_block = results; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(result_block)) + result = 1; + + result_block = result_block->next; + } while (result_block != results); + } + block = block->next; + } while (block != queries_blocks); + } + + DBUG_PRINT("qcache", ("check tables ...")); + for (i=0 ; (int) i < (int) Query_cache_table::TYPES_NUMBER; i++) + { + if ((block = tables_blocks[i])) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_table_get_key((byte*) block, &length, 0); + gptr val = hash_search(&tables, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", + (ulong) block, (ulong) val)); + } + + if (in_blocks(block)) + result = 1; + block=block->next; + } while (block != tables_blocks[i]); + } + } + + DBUG_PRINT("qcache", ("check free blocks")); + for (i = 0; i < mem_bin_num; i++) + { + if ((block = bins[i].free_blocks)) + { + uint count = 0; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(block)) + result = 1; + + count++; + block=block->next; + } while (block != bins[i].free_blocks); + if (count != bins[i].number) + { + DBUG_PRINT("qcache", ("bin[%d].number is %d, but bin have %d blocks", + bins[i].number, count)); + result = 1; + } + } + } + DBUG_ASSERT(result == 0); + STRUCT_UNLOCK(&structure_guard_mutex); + return result; +} + + +my_bool Query_cache::in_blocks(Query_cache_block * point) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->pprev->pnext != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physical list is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pprev, + (ulong) block->pprev->pnext, + (ulong) point)); + //back trace + for(; block != point; block = block->pnext) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->pprev; + } while (block != first_block && block != point); + if (block != first_block) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by pysical list", + (ulong) block, (ulong) block->pprev, (ulong )block->pnext)); + return 1; + } + +err1: + //forward + block = point; + do + { + if (block->pnext->pprev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physicel list is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pnext, + (ulong) block->pnext->pprev, + (ulong) point)); + //back trace + for(; block != point; block = block->pprev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->pnext; + } while (block != first_block); +err2: + return result; +} + + +my_bool Query_cache::in_list(Query_cache_block * root, + Query_cache_block * point, + const char *name) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->prev->next != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->prev, + (ulong) block->prev->next, + (ulong) point)); + //back trace + for(; block != point; block = block->next) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->prev; + } while (block != root && block != point); + if (block != root) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by list '%s' 0x%lx", + (ulong) block, + (ulong) block->prev, (ulong) block->next, + name, (ulong) root)); + return 1; + } +err1: + // forward + block = point; + do + { + if (block->next->prev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->next, + (ulong) block->next->prev, + (ulong) point)); + //back trace + for (; block != point; block = block->prev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->next; + } while (block != root); +err2: + return result; +} + #endif /* DBUG_OFF */ diff --git a/sql/sql_cache.h b/sql/sql_cache.h index c01ea1e21d4..d04fc29a8d9 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -379,6 +379,10 @@ protected: void cache_dump(); void queries_dump(); void tables_dump(); + my_bool check_integrity(); + my_bool in_list(Query_cache_block * root, Query_cache_block * point, + const char *name); + my_bool in_blocks(Query_cache_block * point); #endif friend void query_cache_insert(NET *net, const char *packet, ulong length); friend void query_cache_end_of_result(NET *net); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index c0c0e4244d9..eed6e4e5f81 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -384,6 +384,13 @@ void multi_delete::send_error(uint errcode,const char *err) } +/* + Do delete from other tables. + Returns values: + 0 ok + 1 error +*/ + int multi_delete::do_deletes (bool from_send_error) { int error = 0, counter = 0; @@ -420,7 +427,8 @@ int multi_delete::do_deletes (bool from_send_error) { TABLE_LIST table_list; bzero((char*) &table_list,sizeof(table_list)); - table_list.name=table->table_name; table_list.real_name=table_being_deleted->real_name; + table_list.name=table->table_name; + table_list.real_name=table_being_deleted->real_name; table_list.table=table; table_list.grant=table->grant; table_list.db = table_being_deleted->db; @@ -432,23 +440,20 @@ int multi_delete::do_deletes (bool from_send_error) #endif /* USE_REGENERATE_TABLE */ READ_RECORD info; - error=0; init_read_record(&info,thd,table,NULL,0,0); bool not_trans_safe = some_table_is_not_transaction_safe(delete_tables); while (!(error=info.read_record(&info)) && (!thd->killed || from_send_error || not_trans_safe)) { - error=table->file->delete_row(table->record[0]); - if (error) + if ((error=table->file->delete_row(table->record[0]))) { table->file->print_error(error,MYF(0)); break; } - else - deleted++; + deleted++; } end_read_record(&info); - if (error == -1) + if (error == -1) // End of file error = 0; } return error; @@ -464,7 +469,6 @@ bool multi_delete::send_eof() /* reset used flags */ delete_tables->table->no_keyread=0; - if (error == -1) error = 0; thd->proc_info="end"; if (error) { @@ -477,22 +481,17 @@ bool multi_delete::send_eof() was a non-transaction-safe table involved, since modifications in it cannot be rolled back. */ - if (deleted && - (!error || some_table_is_not_transaction_safe(delete_tables))) + if (deleted || some_table_is_not_transaction_safe(delete_tables)) { mysql_update_log.write(thd,thd->query,thd->query_length); - Query_log_event qinfo(thd, thd->query); - - /* mysql_bin_log is not open if binlogging or replication - is not used */ - - if (mysql_bin_log.is_open() && mysql_bin_log.write(&qinfo) && - !some_table_is_not_transaction_safe(delete_tables)) - error=1; /* Log write failed: roll back - the SQL statement */ - + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query); + if (mysql_bin_log.write(&qinfo) && + !some_table_is_not_transaction_safe(delete_tables)) + error=1; // Log write failed: roll back the SQL statement + } /* Commit or rollback the current SQL statement */ - VOID(ha_autocommit_or_rollback(thd,error > 0)); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 669eee1df4a..12646f0737f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2423,7 +2423,6 @@ mysql_init_query(THD *thd) thd->fatal_error=0; // Safety thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; thd->sent_row_count=thd->examined_row_count=0; - thd->lex.sql_command=SQLCOM_SELECT; DBUG_VOID_RETURN; } @@ -2860,13 +2859,17 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, if (!alias) /* Alias is case sensitive */ if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); - if (lower_case_table_names) - casedn_str(table->table.str); + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ ptr->db= table->db.str ? table->db.str : (thd->db ? thd->db : (char*) ""); - ptr->real_name=table->table.str; ptr->name=alias_str; + if (lower_case_table_names) + { + casedn_str(ptr->db); + casedn_str(table->table.str); + } + ptr->real_name=table->table.str; ptr->lock_type=flags; ptr->updating=updating; if (use_index) @@ -2879,7 +2882,8 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, /* check that used name is unique */ if (flags != TL_IGNORE) { - for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; tables ; + for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; + tables ; tables=tables->next) { if (!strcmp(alias_str,tables->name) && !strcmp(ptr->db, tables->db)) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index b2ffb97fa81..0d8a41e9966 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -27,7 +27,7 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) { - SELECT_LEX *sl, *last_sl=(SELECT_LEX *)NULL, lex_sl; + SELECT_LEX *sl, *last_sl, *lex_sl; ORDER *order; List item_list; TABLE *table; @@ -38,7 +38,10 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) DBUG_ENTER("mysql_union"); /* Fix tables 'to-be-unioned-from' list to point at opened tables */ - for (sl=&lex->select_lex; sl && sl->linkage != NOT_A_SELECT; last_sl=sl, sl=sl->next) + last_sl= &lex->select_lex; + for (sl= last_sl; + sl && sl->linkage != NOT_A_SELECT; + last_sl=sl, sl=sl->next) { for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; cursor; @@ -46,19 +49,27 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) cursor->table= ((TABLE_LIST*) cursor->table)->table; } + /* last_sel now points at the last select where the ORDER BY is stored */ if (sl) { - lex_sl=*sl; - sl=(SELECT_LEX *)NULL; - if (last_sl) last_sl->next=sl; + /* + The found SL is an extra SELECT_LEX argument that contains + the ORDER BY and LIMIT parameter for the whole UNION + */ + lex_sl= sl; + last_sl->next=0; // Remove this extra element + order= (ORDER *) lex_sl->order_list.first; + } + else if (!last_sl->braces) + { + lex_sl= last_sl; // ORDER BY is here + order= (ORDER *) lex_sl->order_list.first; } else - lex_sl.linkage=UNSPECIFIED_TYPE; - - /* Find last select part as it's here ORDER BY and GROUP BY is stored */ - for (last_sl= &lex->select_lex; - last_sl->next; - last_sl=last_sl->next) ; + { + lex_sl=0; + order=0; + } if (lex->select_lex.options & SELECT_DESCRIBE) { @@ -68,7 +79,8 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) res=mysql_select(thd, (TABLE_LIST*) sl->table_list.first, sl->item_list, sl->where, - (sl->braces) ? (ORDER *) sl->order_list.first : (ORDER *) 0, + ((sl->braces) ? + (ORDER *) sl->order_list.first : (ORDER *) 0), (ORDER*) sl->group_list.first, sl->having, (ORDER*) NULL, @@ -79,8 +91,6 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) DBUG_RETURN(0); } - order = (lex_sl.linkage == UNSPECIFIED_TYPE) ? ( (last_sl->braces) ? (ORDER *) 0 : (ORDER *) last_sl->order_list.first) : (ORDER *) lex_sl.order_list.first; - { Item *item; List_iterator it(lex->select_lex.item_list); @@ -162,11 +172,11 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) } if (!thd->fatal_error) // Check if EOM { - if (lex_sl.linkage == NOT_A_SELECT && ( lex_sl.select_limit || lex_sl.offset_limit)) + if (lex_sl) { - thd->offset_limit=lex_sl.offset_limit; - thd->select_limit=lex_sl.select_limit+lex_sl.offset_limit; - if (thd->select_limit < lex_sl.select_limit) + thd->offset_limit=lex_sl->offset_limit; + thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit; + if (thd->select_limit < lex_sl->select_limit) thd->select_limit= HA_POS_ERROR; // no limit if (thd->select_limit == HA_POS_ERROR) thd->options&= ~OPTION_FOUND_ROWS; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1d4c39823ca..1dbdda28ffc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1348,9 +1348,13 @@ table_to_table: select: - SELECT_SYM select_part2 { Select->braces=false; } union + select_init { Lex->sql_command=SQLCOM_SELECT; } + +select_init: + SELECT_SYM select_part2 { Select->braces=false; } union | - '(' SELECT_SYM select_part2 ')' {Select->braces=true;} union_opt + '(' SELECT_SYM select_part2 ')' { Select->braces=true;} union_opt + select_part2: { @@ -1643,10 +1647,14 @@ simple_expr: { $$= new Item_func_decode($3,$5.str); } | ENCODE_SYM '(' expr ',' TEXT_STRING ')' { $$= new Item_func_encode($3,$5.str); } - | DES_DECRYPT '(' expr ',' expr ')' - { $$= new Item_func_des_decrypt($3,$5); } - | DES_ENCRYPT '(' expr ',' expr ')' - { $$= new Item_func_des_encrypt($3,$5); } + | DES_DECRYPT '(' expr ')' + { $$= new Item_func_des_decrypt($3); } + | DES_DECRYPT '(' expr ',' expr ')' + { $$= new Item_func_des_decrypt($3,$5); } + | DES_ENCRYPT '(' expr ')' + { $$= new Item_func_des_encrypt($3); } + | DES_ENCRYPT '(' expr ',' expr ')' + { $$= new Item_func_des_encrypt($3,$5); } | EXPORT_SET '(' expr ',' expr ',' expr ')' { $$= new Item_func_export_set($3, $5, $7); } | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')' @@ -3573,12 +3581,12 @@ union_list: net_printf(&lex->thd->net, ER_WRONG_USAGE,"UNION","INTO"); YYABORT; } - if (lex->select->linkage==NOT_A_SELECT) + if (lex->select->linkage == NOT_A_SELECT) YYABORT; mysql_new_select(lex); lex->select->linkage=UNION_TYPE; } - select + select_init union_opt: union {}