Commit 161942e3 authored by unknown's avatar unknown

Merge with 4.0.12


Docs/internals.texi:
  Auto merged
include/my_global.h:
  Auto merged
include/mysql_com.h:
  Auto merged
innobase/row/row0mysql.c:
  Auto merged
innobase/row/row0sel.c:
  Auto merged
libmysql/Makefile.am:
  Auto merged
libmysqld/Makefile.am:
  Auto merged
libmysqld/lib_vio.c:
  Auto merged
mysql-test/r/heap.result:
  Auto merged
mysql-test/r/innodb.result:
  Auto merged
mysql-test/t/heap.test:
  Auto merged
sql/ha_innodb.h:
  Auto merged
sql/ha_myisam.cc:
  Auto merged
BitKeeper/deleted/.del-password.c~76f30876e68eddb4:
  Auto merged
sql/handler.cc:
  Auto merged
sql/handler.h:
  Auto merged
sql/item_func.cc:
  Auto merged
sql/key.cc:
  Auto merged
sql/lex.h:
  Auto merged
sql/log.cc:
  Auto merged
sql/mysqld.cc:
  Auto merged
sql/slave.cc:
  Auto merged
sql/slave.h:
  Auto merged
sql/sql_class.cc:
  Auto merged
sql/sql_repl.cc:
  Auto merged
sql/sql_show.cc:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
sql/table.cc:
  Auto merged
strings/strto.c:
  Auto merged
parents d7bedeb9 7517a59a
......@@ -2798,11 +2798,11 @@ Storage: same as TINYINT.
@strong{DATE}
@itemize @bullet
@item
Storage: fixed-length series of binary integers, always three bytes
long.
Storage: 3 byte integer, low byte first.
Packed as: 'day + month*32 + year*16*32'
@item
Example: a DATE column containing '0001-01-01' looks like:@*
@code{hexadecimal 21 02 00}
Example: a DATE column containing '1962-01-02' looks like:@*
@code{hexadecimal 22 54 0F}
@end itemize
@strong{DATETIME}
......@@ -2821,16 +2821,19 @@ Example: a DATETIME column for '0001-01-01 01:01:01' looks like:@*
@strong{TIME}
@itemize @bullet
@item
Storage: a value offset from 8385959, always three bytes long.
Storage: 3 bytes, low byte first.
This is stored as seconds: days*24*3600+hours*3600+minutes*60+seconds
@item
Example: a TIME column containing '01:01:01' looks like:@*
@code{hexadecimal 75 27 00}
Example: a TIME column containing '1 02:03:04' (1 day 2 hour 3 minutes and 4 seconds) looks like:@*
@code{hexadecimal 58 6E 01}
@end itemize
@strong{TIMESTAMP}
@itemize @bullet
@item
Storage: four bytes long (NOTE TO SELF: not figured out)
Storage: 4 bytes, low byte first.
Stored as unix @code{time()}, which is seconds since the Epoch
(00:00:00 UTC, January 1, 1970).
@item
Example: a TIMESTAMP column containing '2003-01-01 01:01:01' looks like:@*
@code{hexadecimal 4D AE 12 23}
......
......@@ -40,7 +40,7 @@
#include <signal.h>
#include <violite.h>
const char *VER= "13.4";
const char *VER= "13.5";
/* Don't try to make a nice table if the data is too big */
#define MAX_COLUMN_LENGTH 1024
......@@ -799,6 +799,7 @@ static int get_options(int argc, char **argv)
}
if (argc == 1)
{
skip_updates= 0;
my_free(current_db, MYF(MY_ALLOW_ZERO_PTR));
current_db= my_strdup(*argv, MYF(MY_WME));
}
......
/* Copyright (C) 2000 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* password checking routines */
/*****************************************************************************
The main idea is that no password are sent between client & server on
connection and that no password are saved in mysql in a decodable form.
On connection a random string is generated and sent to the client.
The client generates a new string with a random generator inited with
the hash values from the password and the sent string.
This 'check' string is sent to the server where it is compared with
a string generated from the stored hash_value of the password and the
random string.
The password is saved (in user.password) by using the PASSWORD() function in
mysql.
Example:
update user set password=PASSWORD("hello") where user="test"
This saves a hashed number as a string in the password field.
*****************************************************************************/
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
#include "mysql.h"
void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
{ /* For mysql 3.21.# */
#ifdef HAVE_purify
bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
#endif
rand_st->max_value= 0x3FFFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
rand_st->seed1=seed1%rand_st->max_value ;
rand_st->seed2=seed2%rand_st->max_value;
}
static void old_randominit(struct rand_struct *rand_st,ulong seed1)
{ /* For mysql 3.20.# */
rand_st->max_value= 0x01FFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
seed1%=rand_st->max_value;
rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
}
double rnd(struct rand_struct *rand_st)
{
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
return (((double) rand_st->seed1)/rand_st->max_value_dbl);
}
void hash_password(ulong *result, const char *password)
{
register ulong nr=1345345333L, add=7, nr2=0x12345671L;
ulong tmp;
for (; *password ; password++)
{
if (*password == ' ' || *password == '\t')
continue; /* skipp space in password */
tmp= (ulong) (uchar) *password;
nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
nr2+=(nr2 << 8) ^ nr;
add+=tmp;
}
result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
result[1]=nr2 & (((ulong) 1L << 31) -1L);
return;
}
void make_scrambled_password(char *to,const char *password)
{
ulong hash_res[2];
hash_password(hash_res,password);
sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}
static inline uint char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
X >= 'A' && X <= 'Z' ? X-'A'+10 :
X-'a'+10);
}
/*
** This code assumes that len(password) is divideable with 8 and that
** res is big enough (2 in mysql)
*/
void get_salt_from_password(ulong *res,const char *password)
{
res[0]=res[1]=0;
if (password)
{
while (*password)
{
ulong val=0;
uint i;
for (i=0 ; i < 8 ; i++)
val=(val << 4)+char_val(*password++);
*res++=val;
}
}
return;
}
void make_password_from_salt(char *to, ulong *hash_res)
{
sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}
/*
* Genererate a new message based on message and password
* The same thing is done in client and server and the results are checked.
*/
char *scramble(char *to,const char *message,const char *password,
my_bool old_ver)
{
struct rand_struct rand_st;
ulong hash_pass[2],hash_message[2];
if (password && password[0])
{
char *to_start=to;
hash_password(hash_pass,password);
hash_password(hash_message,message);
if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
while (*message++)
*to++= (char) (floor(rnd(&rand_st)*31)+64);
if (!old_ver)
{ /* Make it harder to break */
char extra=(char) (floor(rnd(&rand_st)*31));
while (to_start != to)
*(to_start++)^=extra;
}
}
*to=0;
return to;
}
my_bool check_scramble(const char *scrambled, const char *message,
ulong *hash_pass, my_bool old_ver)
{
struct rand_struct rand_st;
ulong hash_message[2];
char buff[16],*to,extra; /* Big enough for check */
const char *pos;
hash_password(hash_message,message);
if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
to=buff;
for (pos=scrambled ; *pos ; pos++)
*to++=(char) (floor(rnd(&rand_st)*31)+64);
if (old_ver)
extra=0;
else
extra=(char) (floor(rnd(&rand_st)*31));
to=buff;
while (*scrambled)
{
if (*scrambled++ != (char) (*to++ ^ extra))
return 1; /* Wrong password */
}
return 0;
}
......@@ -16,7 +16,7 @@
# MA 02111-1307, USA
BUILT_SOURCES = mysql_version.h m_ctype.h my_config.h
pkginclude_HEADERS = dbug.h m_string.h my_sys.h my_list.h my_xml.h \
pkginclude_HEADERS = my_dbug.h m_string.h my_sys.h my_list.h my_xml.h \
mysql.h mysql_com.h mysqld_error.h mysql_embed.h \
my_semaphore.h my_pthread.h my_no_pthread.h raid.h \
errmsg.h my_global.h my_net.h my_alloc.h \
......
......@@ -417,7 +417,7 @@ typedef unsigned short ushort;
#define DBUG_OFF
#endif
#include <dbug.h>
#include <my_dbug.h>
#define MIN_ARRAY_SIZE 0 /* Zero or One. Gcc allows zero*/
#define ASCII_BITS_USED 8 /* Bit char used */
......
......@@ -298,7 +298,7 @@ extern unsigned long net_buffer_length;
void randominit(struct rand_struct *,unsigned long seed1,
unsigned long seed2);
double rnd(struct rand_struct *);
double my_rnd(struct rand_struct *);
void make_scrambled_password(char *to,const char *password,
my_bool force_old_scramble,struct rand_struct *rand_st);
int get_password_length(my_bool force_old_scramble);
......
......@@ -6,7 +6,7 @@ Contains also create table and other data dictionary operations.
Created 9/17/2000 Heikki Tuuri
*******************************************************/
#include "row0mysql.h"
#ifdef UNIV_NONINL
......
......@@ -48,10 +48,6 @@ link_sources:
rm -f $(srcdir)/$$f; \
@LN_CP_F@ $(srcdir)/../strings/$$f $(srcdir)/$$f; \
done; \
for f in $$qs; do \
rm -f $(srcdir)/$$f; \
@LN_CP_F@ $(srcdir)/../sql/$$f $(srcdir)/$$f; \
done; \
for f in $$ds; do \
rm -f $(srcdir)/$$f; \
@LN_CP_F@ $(srcdir)/../dbug/$$f $(srcdir)/$$f; \
......@@ -61,7 +57,9 @@ link_sources:
@LN_CP_F@ $(srcdir)/../mysys/$$f $(srcdir)/$$f; \
done; \
rm -f $(srcdir)/net.c; \
@LN_CP_F@ $(srcdir)/../sql/net_serv.cc $(srcdir)/net.c
@LN_CP_F@ $(srcdir)/../sql/net_serv.cc $(srcdir)/net.c ; \
rm -f $(srcdir)/password.c; \
@LN_CP_F@ $(srcdir)/../sql/password.c $(srcdir)/password.c
# This part requires GNUmake
#
......
......@@ -23,7 +23,6 @@
#include "mysql.h"
#include <m_string.h>
#include <m_ctype.h>
#include <dbug.h>
#if defined(HAVE_BROKEN_GETPASS) && !defined(HAVE_GETPASSPHRASE)
#undef HAVE_GETPASS
......
......@@ -32,14 +32,14 @@ noinst_LIBRARIES = libmysqld_int.a
pkglib_LIBRARIES = libmysqld.a
SUBDIRS = . examples
libmysqld_sources= libmysqld.c lib_sql.cc
libmysqlsources = errmsg.c get_password.c password.c
libmysqlsources = errmsg.c get_password.c
noinst_HEADERS = embedded_priv.h
sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \
ha_innodb.cc ha_berkeley.cc ha_heap.cc ha_isam.cc ha_isammrg.cc \
ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \
hostname.cc init.cc \
hostname.cc init.cc password.c \
item.cc item_buff.cc item_cmpfunc.cc item_create.cc \
item_func.cc item_strfunc.cc item_sum.cc item_timefunc.cc \
item_uniq.cc item_subselect.cc item_row.cc\
......
......@@ -33,7 +33,6 @@
#include <violite.h>
#include <my_net.h>
#include <m_string.h>
#include <dbug.h>
#include <assert.h>
#ifndef __WIN__
......
......@@ -253,3 +253,26 @@ Incorrect table name 'a/a'
drop table t1, t2, t3;
drop table t3;
drop database test_$1;
SET SESSION table_type="heap";
SELECT @@table_type;
@@table_type
HEAP
CREATE TABLE t1 (a int not null);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL default '0'
) TYPE=HEAP
drop table t1;
SET SESSION table_type="gemini";
SELECT @@table_type;
@@table_type
GEMINI
CREATE TABLE t1 (a int not null);
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) NOT NULL default '0'
) TYPE=MyISAM
SET SESSION table_type=default;
drop table t1;
......@@ -23,7 +23,7 @@ a b
4 6
alter table t1 add c int not null, add key (c,a);
drop table t1;
create table t1 (a int not null,b int not null, primary key (a)) type=heap comment="testing heaps";
create table t1 (a int not null,b int not null, primary key (a)) type=memory comment="testing heaps";
insert into t1 values(1,1),(2,2),(3,3),(4,4);
delete from t1 where a > 0;
select * from t1;
......
drop table if exists t1,t2;
drop table if exists t1,t2,t3;
create table t1 (id int unsigned not null auto_increment, code tinyint unsigned not null, name char(20) not null, primary key (id), key (code), unique (name)) type=innodb;
insert into t1 (code, name) values (1, 'Tim'), (1, 'Monty'), (2, 'David'), (2, 'Erik'), (3, 'Sasha'), (3, 'Jeremy'), (4, 'Matt');
select id, code, name from t1 order by id;
......@@ -1093,3 +1093,113 @@ SELECT * from t1;
id
3
DROP TABLE t1,t2;
set autocommit=0;
CREATE TABLE t1 (id CHAR(15) NOT NULL, value CHAR(40) NOT NULL, PRIMARY KEY(id)) TYPE=InnoDB;
CREATE TABLE t2 (id CHAR(15) NOT NULL, value CHAR(40) NOT NULL, PRIMARY KEY(id)) TYPE=InnoDB;
CREATE TABLE t3 (id1 CHAR(15) NOT NULL, id2 CHAR(15) NOT NULL, PRIMARY KEY(id1, id2)) TYPE=InnoDB;
INSERT INTO t3 VALUES("my-test-1", "my-test-2");
COMMIT;
INSERT INTO t1 VALUES("this-key", "will disappear");
INSERT INTO t2 VALUES("this-key", "will also disappear");
DELETE FROM t3 WHERE id1="my-test-1";
SELECT * FROM t1;
id value
this-key will disappear
SELECT * FROM t2;
id value
this-key will also disappear
SELECT * FROM t3;
id1 id2
ROLLBACK;
SELECT * FROM t1;
id value
SELECT * FROM t2;
id value
SELECT * FROM t3;
id1 id2
my-test-1 my-test-2
SELECT * FROM t3 WHERE id1="my-test-1" LOCK IN SHARE MODE;
id1 id2
my-test-1 my-test-2
COMMIT;
set autocommit=1;
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (a int not null primary key, b int not null, unique (b)) type=innodb;
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
UPDATE t1 set a=a+100 where b between 2 and 3 and a < 1000;
SELECT * from t1;
a b
1 1
102 2
103 3
4 4
5 5
6 6
7 7
8 8
9 9
drop table t1;
CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb;
CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb;
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
update t1,t2 set t1.a=t1.a+100;
select * from t1;
a b
101 1
102 2
103 3
104 4
105 5
106 6
107 7
108 8
109 9
update t1,t2 set t1.a=t1.a+100 where t1.a=101;
select * from t1;
a b
201 1
102 2
103 3
104 4
105 5
106 6
107 7
108 8
109 9
update t1,t2 set t1.b=t1.b+10 where t1.b=2;
select * from t1;
a b
201 1
103 3
104 4
105 5
106 6
107 7
108 8
109 9
102 12
update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
select * from t1;
a b
201 1
103 5
104 6
106 6
105 7
107 7
108 8
109 9
102 12
select * from t2;
a b
1 5
2 5
3 5
4 5
5 5
6 5
7 5
8 5
9 5
drop table t1,t2;
......@@ -260,3 +260,67 @@ INSERT INTO t3 VALUES (1,'jedan'),(2,'dva');
update t1,t2 set t1.naziv="aaaa" where t1.broj=t2.broj;
update t1,t2,t3 set t1.naziv="bbbb", t2.naziv="aaaa" where t1.broj=t2.broj and t2.broj=t3.broj;
drop table t1,t2,t3;
CREATE TABLE t1 (a int not null primary key, b int not null, key (b));
CREATE TABLE t2 (a int not null primary key, b int not null, key (b));
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
update t1,t2 set t1.a=t1.a+100;
select * from t1;
a b
101 1
102 2
103 3
104 4
105 5
106 6
107 7
108 8
109 9
update t1,t2 set t1.a=t1.a+100 where t1.a=101;
select * from t1;
a b
201 1
102 2
103 3
104 4
105 5
106 6
107 7
108 8
109 9
update t1,t2 set t1.b=t1.b+10 where t1.b=2;
select * from t1;
a b
201 1
102 12
103 3
104 4
105 5
106 6
107 7
108 8
109 9
update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
select * from t1;
a b
201 1
102 12
103 5
104 6
105 7
106 6
107 7
108 8
109 9
select * from t2;
a b
1 3
2 3
3 3
4 3
5 3
6 3
7 3
8 3
9 3
drop table t1,t2;
slave stop;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
slave start;
stop slave;
create table t1 (a int);
reset slave;
start slave;
select master_pos_wait('master-bin.001',5000,45)=-1;
master_pos_wait('master-bin.001',5000,45)=-1
0
......@@ -167,3 +167,18 @@ drop table t1, t2, t3;
drop table t3;
drop database test_$1;
#
# Test default table type
#
SET SESSION table_type="heap";
SELECT @@table_type;
CREATE TABLE t1 (a int not null);
show create table t1;
drop table t1;
# Test what happens when using a non existing table type
SET SESSION table_type="gemini";
SELECT @@table_type;
CREATE TABLE t1 (a int not null);
show create table t1;
SET SESSION table_type=default;
drop table t1;
......@@ -20,7 +20,7 @@ select * from t1;
alter table t1 add c int not null, add key (c,a);
drop table t1;
create table t1 (a int not null,b int not null, primary key (a)) type=heap comment="testing heaps";
create table t1 (a int not null,b int not null, primary key (a)) type=memory comment="testing heaps";
insert into t1 values(1,1),(2,2),(3,3),(4,4);
delete from t1 where a > 0;
select * from t1;
......
......@@ -5,7 +5,7 @@
#
--disable_warnings
drop table if exists t1,t2;
drop table if exists t1,t2,t3;
--enable_warnings
create table t1 (id int unsigned not null auto_increment, code tinyint unsigned not null, name char(20) not null, primary key (id), key (code), unique (name)) type=innodb;
......@@ -728,3 +728,74 @@ SELECT * from t1;
UPDATE t1,t2 SET t1.id=t1.id+1 where t1.id!=t2.id;
SELECT * from t1;
DROP TABLE t1,t2;
#
# Test of range_optimizer
#
set autocommit=0;
CREATE TABLE t1 (id CHAR(15) NOT NULL, value CHAR(40) NOT NULL, PRIMARY KEY(id)) TYPE=InnoDB;
CREATE TABLE t2 (id CHAR(15) NOT NULL, value CHAR(40) NOT NULL, PRIMARY KEY(id)) TYPE=InnoDB;
CREATE TABLE t3 (id1 CHAR(15) NOT NULL, id2 CHAR(15) NOT NULL, PRIMARY KEY(id1, id2)) TYPE=InnoDB;
INSERT INTO t3 VALUES("my-test-1", "my-test-2");
COMMIT;
INSERT INTO t1 VALUES("this-key", "will disappear");
INSERT INTO t2 VALUES("this-key", "will also disappear");
DELETE FROM t3 WHERE id1="my-test-1";
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
ROLLBACK;
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
SELECT * FROM t3 WHERE id1="my-test-1" LOCK IN SHARE MODE;
COMMIT;
set autocommit=1;
DROP TABLE t1,t2,t3;
#
# Check update with conflicting key
#
CREATE TABLE t1 (a int not null primary key, b int not null, unique (b)) type=innodb;
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
# We need the a < 1000 test here to quard against the halloween problems
UPDATE t1 set a=a+100 where b between 2 and 3 and a < 1000;
SELECT * from t1;
drop table t1;
#
# Test multi update with different join methods
#
CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb;
CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb;
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
# Full join, without key
update t1,t2 set t1.a=t1.a+100;
select * from t1;
# unique key
update t1,t2 set t1.a=t1.a+100 where t1.a=101;
select * from t1;
# ref key
update t1,t2 set t1.b=t1.b+10 where t1.b=2;
select * from t1;
# Range key (in t1)
update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
select * from t1;
select * from t2;
drop table t1,t2;
......@@ -232,3 +232,31 @@ INSERT INTO t3 VALUES (1,'jedan'),(2,'dva');
update t1,t2 set t1.naziv="aaaa" where t1.broj=t2.broj;
update t1,t2,t3 set t1.naziv="bbbb", t2.naziv="aaaa" where t1.broj=t2.broj and t2.broj=t3.broj;
drop table t1,t2,t3;
#
# Test multi update with different join methods
#
CREATE TABLE t1 (a int not null primary key, b int not null, key (b));
CREATE TABLE t2 (a int not null primary key, b int not null, key (b));
INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9);
# Full join, without key
update t1,t2 set t1.a=t1.a+100;
select * from t1;
# unique key
update t1,t2 set t1.a=t1.a+100 where t1.a=101;
select * from t1;
# ref key
update t1,t2 set t1.b=t1.b+10 where t1.b=2;
select * from t1;
# Range key (in t1)
update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5;
select * from t1;
select * from t2;
drop table t1,t2;
-O relay_log_space_limit=1024
\ No newline at end of file
# The slave is started with relay_log_space_limit=1024 bytes,
# to force the deadlock
source include/master-slave.inc;
connection slave;
stop slave;
connection master;
create table t1 (a int);
let $1=200;
disable_query_log;
while ($1)
{
# eval means expand $ expressions
eval insert into t1 values( $1 );
dec $1;
}
# This will generate one 10kB master's binlog
enable_query_log;
save_master_pos;
connection slave;
reset slave;
start slave;
# The I/O thread stops filling the relay log when
# it's 1kB. And the SQL thread cannot purge this relay log
# as purge is done only when the SQL thread switches to another
# relay log, which does not exist here.
# So we should have a deadlock.
# if it is not resolved automatically we'll detect
# it with master_pos_wait that waits for farther than 1kB;
# it will timeout after 45 seconds;
# also the slave will probably not cooperate to shutdown
# (as 2 threads are locked)
select master_pos_wait('master-bin.001',5000,45)=-1;
......@@ -82,8 +82,7 @@ class ha_innobase: public handler
HA_PRIMARY_KEY_IN_READ_INDEX |
HA_DROP_BEFORE_CREATE |
HA_NO_PREFIX_CHAR_KEYS |
HA_TABLE_SCAN_ON_INDEX |
HA_NOT_MULTI_UPDATE),
HA_TABLE_SCAN_ON_INDEX),
last_dup_key((uint) -1),
start_of_scan(0)
{
......
......@@ -1258,6 +1258,35 @@ longlong ha_myisam::get_auto_increment()
}
/*
Find out how many rows there is in the given range
SYNOPSIS
records_in_range()
inx Index to use
start_key Start of range. Null pointer if from first key
start_key_len Length of start key
start_search_flag Flag if start key should be included or not
end_key End of range. Null pointer if to last key
end_key_len Length of end key
end_search_flag Flag if start key should be included or not
NOTES
start_search_flag can have one of the following values:
HA_READ_KEY_EXACT Include the key in the range
HA_READ_AFTER_KEY Don't include key in range
end_search_flag can have one of the following values:
HA_READ_BEFORE_KEY Don't include key in range
HA_READ_AFTER_KEY Include all 'end_key' values in the range
RETURN
HA_POS_ERROR Something is wrong with the index tree.
0 There is no matching keys in the given range
number > 0 There is approximately 'number' matching rows in
the range.
*/
ha_rows ha_myisam::records_in_range(int inx,
const byte *start_key,uint start_key_len,
enum ha_rkey_function start_search_flag,
......@@ -1272,6 +1301,7 @@ ha_rows ha_myisam::records_in_range(int inx,
end_search_flag);
}
int ha_myisam::ft_read(byte * buf)
{
int error;
......
......@@ -121,8 +121,15 @@ handler *get_new_handler(TABLE *table, enum db_type db_type)
#endif
case DB_TYPE_HEAP:
return new ha_heap(table);
case DB_TYPE_MYISAM:
default: // should never happen
{
enum db_type def=(enum db_type) current_thd->variables.table_type;
/* Try first with 'default table type' */
if (db_type != def)
return get_new_handler(table, def);
}
/* Fall back to MyISAM */
case DB_TYPE_MYISAM:
return new ha_myisam(table);
case DB_TYPE_MRG_MYISAM:
return new ha_myisammrg(table);
......
......@@ -67,7 +67,6 @@
#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2)
#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2)
#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2)
#define HA_NOT_MULTI_UPDATE (HA_NO_AUTO_INCREMENT*2)
/*
Next record gives next record according last record read (even
......
......@@ -851,7 +851,7 @@ void Item_func_rand::fix_length_and_dec()
double Item_func_rand::val()
{
return rnd(rand);
return my_rnd(rand);
}
longlong Item_func_sign::val_int()
......
......@@ -271,5 +271,13 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
return 1;
}
}
/*
If table handler has primary key as part of the index, check that primary
key is not updated
*/
if (idx != table->primary_key && table->primary_key < MAX_KEY &&
(table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
return check_if_key_used(table, table->primary_key, fields);
return 0;
}
......@@ -257,6 +257,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMINT", SYM(MEDIUMINT),0,0},
{ "MERGE", SYM(MERGE_SYM),0,0},
{ "MEDIUM", SYM(MEDIUM_SYM),0,0},
{ "MEMORY", SYM(MEMORY_SYM),0,0},
{ "MIDDLEINT", SYM(MEDIUMINT),0,0}, /* For powerbuilder */
{ "MIN_ROWS", SYM(MIN_ROWS),0,0},
{ "MINUTE", SYM(MINUTE_SYM),0,0},
......
......@@ -654,6 +654,8 @@ int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli)
*/
pthread_mutex_lock(&rli->log_space_lock);
rli->log_space_total -= rli->relay_log_pos;
//tell the I/O thread to take the relay_log_space_limit into account
rli->ignore_log_space_limit= 0;
pthread_mutex_unlock(&rli->log_space_lock);
pthread_cond_broadcast(&rli->log_space_cond);
......
......@@ -2761,7 +2761,7 @@ static void create_new_thread(THD *thd)
max_used_connections=thread_count-delayed_insert_threads;
thd->thread_id=thread_id++;
for (uint i=0; i < 8 ; i++) // Generate password teststring
thd->scramble[i]= (char) (rnd(&sql_rand)*94+33);
thd->scramble[i]= (char) (my_rnd(&sql_rand)*94+33);
thd->scramble[8]=0;
// Back it up as old clients may need it
memcpy(thd->old_scramble,thd->scramble,9);
......
......@@ -125,7 +125,7 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1)
Generated pseudo random number
*/
double rnd(struct rand_struct *rand_st)
double my_rnd(struct rand_struct *rand_st)
{
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
......@@ -435,7 +435,7 @@ char get_password_version(const char *password)
inline uint char_val(char X)
static inline unsigned int char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
X >= 'A' && X <= 'Z' ? X-'A'+10 :
......@@ -652,10 +652,10 @@ char *scramble(char *to,const char *message,const char *password,
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
while (*msg++)
*to++= (char) (floor(rnd(&rand_st)*31)+64);
*to++= (char) (floor(my_rnd(&rand_st)*31)+64);
if (!old_ver)
{ /* Make it harder to break */
char extra=(char) (floor(rnd(&rand_st)*31));
char extra=(char) (floor(my_rnd(&rand_st)*31));
while (to_start != to)
*(to_start++)^=extra;
}
......@@ -711,11 +711,11 @@ my_bool check_scramble(const char *scrambled, const char *message,
hash_pass[1] ^ hash_message[1]);
to=buff;
for (pos=scrambled ; *pos ; pos++)
*to++=(char) (floor(rnd(&rand_st)*31)+64);
*to++=(char) (floor(my_rnd(&rand_st)*31)+64);
if (old_ver)
extra=0;
else
extra=(char) (floor(rnd(&rand_st)*31));
extra=(char) (floor(my_rnd(&rand_st)*31));
to=buff;
while (*scrambled)
{
......
......@@ -263,7 +263,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
if (log) // If not first log
{
if (strcmp(log, rli->linfo.log_file_name))
rli->skip_log_purge=1; // Different name; Don't purge
rli->skip_log_purge= 1; // Different name; Don't purge
if (rli->relay_log.find_log_pos(&rli->linfo, log, 1))
{
*errmsg="Could not find target log during relay log initialization";
......@@ -298,6 +298,12 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
my_b_seek(rli->cur_log,(off_t)pos);
err:
/*
If we don't purge, we can't honour relay_log_space_limit ;
silently discard it
*/
if (rli->skip_log_purge)
rli->log_space_limit= 0;
pthread_cond_broadcast(&rli->data_cond);
if (need_data_lock)
pthread_mutex_unlock(&rli->data_lock);
......@@ -1386,7 +1392,8 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli)
thd->proc_info = "Waiting for relay log space to free";
while (rli->log_space_limit < rli->log_space_total &&
!(slave_killed=io_slave_killed(thd,mi)))
!(slave_killed=io_slave_killed(thd,mi)) &&
!rli->ignore_log_space_limit)
{
pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
}
......@@ -1667,7 +1674,7 @@ bool flush_master_info(MASTER_INFO* mi)
st_relay_log_info::st_relay_log_info()
:info_fd(-1), cur_log_fd(-1), master_log_pos(0), save_temporary_tables(0),
cur_log_old_open_count(0), log_space_total(0),
cur_log_old_open_count(0), log_space_total(0), ignore_log_space_limit(0),
slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0),
sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0),
slave_running(0), skip_log_purge(0),
......@@ -2378,7 +2385,8 @@ reconnect done to recover from failed read");
}
flush_master_info(mi);
if (mi->rli.log_space_limit && mi->rli.log_space_limit <
mi->rli.log_space_total)
mi->rli.log_space_total &&
!mi->rli.ignore_log_space_limit)
if (wait_for_relay_log_space(&mi->rli))
{
sql_print_error("Slave I/O thread aborted while waiting for relay \
......@@ -2491,6 +2499,10 @@ extern "C" pthread_handler_decl(handle_slave_sql,arg)
pthread_cond_broadcast(&rli->start_cond);
// This should always be set to 0 when the slave thread is started
rli->pending = 0;
//tell the I/O thread to take relay_log_space_limit into account from now on
rli->ignore_log_space_limit= 0;
if (init_relay_log_pos(rli,
rli->relay_log_name,
rli->relay_log_pos,
......@@ -3199,11 +3211,41 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
update. If we do not, show slave status will block
*/
pthread_mutex_unlock(&rli->data_lock);
/* Note that wait_for_update unlocks lock_log ! */
rli->relay_log.wait_for_update(rli->sql_thd);
// re-acquire data lock since we released it earlier
pthread_mutex_lock(&rli->data_lock);
/*
Possible deadlock :
- the I/O thread has reached log_space_limit
- the SQL thread has read all relay logs, but cannot purge for some
reason:
* it has already purged all logs except the current one
* there are other logs than the current one but they're involved in
a transaction that finishes in the current one (or is not finished)
Solution :
Wake up the possibly waiting I/O thread, and set a boolean asking
the I/O thread to temporarily ignore the log_space_limit
constraint, because we do not want the I/O thread to block because of
space (it's ok if it blocks for any other reason (e.g. because the
master does not send anything). Then the I/O thread stops waiting
and reads more events.
The SQL thread decides when the I/O thread should take log_space_limit
into account again : ignore_log_space_limit is reset to 0
in purge_first_log (when the SQL thread purges the just-read relay
log), and also when the SQL thread starts. We should also reset
ignore_log_space_limit to 0 when the user does RESET SLAVE, but in
fact, no need as RESET SLAVE requires that the slave
be stopped, and when the SQL thread is later restarted
ignore_log_space_limit will be reset to 0.
*/
pthread_mutex_lock(&rli->log_space_lock);
// prevent the I/O thread from blocking next times
rli->ignore_log_space_limit= 1;
// If the I/O thread is blocked, unblock it
pthread_cond_broadcast(&rli->log_space_cond);
pthread_mutex_unlock(&rli->log_space_lock);
// Note that wait_for_update unlocks lock_log !
rli->relay_log.wait_for_update(rli->sql_thd);
// re-acquire data lock since we released it earlier
pthread_mutex_lock(&rli->data_lock);
continue;
}
/*
......
......@@ -156,7 +156,14 @@ typedef struct st_relay_log_info
extra offset to be added to the position.
*/
ulonglong relay_log_pos, pending;
/*
Handling of the relay_log_space_limit optional constraint.
ignore_log_space_limit is used to resolve a deadlock between I/O and SQL
threads, it makes the I/O thread temporarily forget about the constraint
*/
ulonglong log_space_limit,log_space_total;
bool ignore_log_space_limit;
/*
InnoDB internally stores the master log position it has processed
......
......@@ -186,7 +186,7 @@ THD::THD():user_time(0), is_fatal_error(0),
*/
{
pthread_mutex_lock(&LOCK_thread_count);
ulong tmp=(ulong) (rnd(&sql_rand) * 0xffffffff); /* make all bits random */
ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
pthread_mutex_unlock(&LOCK_thread_count);
randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
}
......
......@@ -46,7 +46,7 @@ void SQL_CRYPT::crypt_init(ulong *rand_nr)
for (i=0 ; i<= 255 ; i++)
{
int idx= (uint) (rnd(&rand)*255.0);
int idx= (uint) (my_rnd(&rand)*255.0);
char a= decode_buff[idx];
decode_buff[idx]= decode_buff[i];
decode_buff[+i]=a;
......@@ -62,7 +62,7 @@ void SQL_CRYPT::encode(char *str,uint length)
{
for (uint i=0; i < length; i++)
{
shift^=(uint) (rnd(&rand)*255.0);
shift^=(uint) (my_rnd(&rand)*255.0);
uint idx= (uint) (uchar) str[0];
*str++ = (char) ((uchar) encode_buff[idx] ^ shift);
shift^= idx;
......@@ -74,7 +74,7 @@ void SQL_CRYPT::decode(char *str,uint length)
{
for (uint i=0; i < length; i++)
{
shift^=(uint) (rnd(&rand)*255.0);
shift^=(uint) (my_rnd(&rand)*255.0);
uint idx= (uint) ((unsigned char) str[0] ^ shift);
*str = decode_buff[idx];
shift^= (uint) (uchar) *str++;
......
......@@ -563,7 +563,10 @@ check_connections(THD *thd)
thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
/* Cut very long hostnames to avoid possible overflows */
if (thd->host)
{
thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
thd->host_or_ip= thd->host;
}
if (connect_errors > max_connect_errors)
return(ER_HOST_IS_BLOCKED);
}
......
......@@ -900,22 +900,21 @@ int change_master(THD* thd, MASTER_INFO* mi)
if (lex_mi->relay_log_name)
{
need_relay_log_purge = 0;
mi->rli.skip_log_purge=1;
need_relay_log_purge= 0;
strmake(mi->rli.relay_log_name,lex_mi->relay_log_name,
sizeof(mi->rli.relay_log_name)-1);
}
if (lex_mi->relay_log_pos)
{
need_relay_log_purge=0;
need_relay_log_purge= 0;
mi->rli.relay_log_pos=lex_mi->relay_log_pos;
}
flush_master_info(mi);
if (need_relay_log_purge)
{
mi->rli.skip_log_purge=0;
mi->rli.skip_log_purge= 0;
thd->proc_info="purging old relay logs";
if (purge_relay_logs(&mi->rli, thd,
0 /* not only reset, but also reinit */,
......@@ -929,6 +928,7 @@ int change_master(THD* thd, MASTER_INFO* mi)
else
{
const char* msg;
mi->rli.skip_log_purge= 1;
/* Relay log is already initialized */
if (init_relay_log_pos(&mi->rli,
mi->rli.relay_log_name,
......
......@@ -1334,10 +1334,10 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
{
if ((thd_info->host= thd->alloc(LIST_PROCESS_HOST_LEN+1)))
my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
"%s:%u", thd->host_or_ip, tmp->peer_port);
"%s:%u", tmp->host_or_ip, tmp->peer_port);
}
else
thd_info->host= thd->strdup(thd->host_or_ip);
thd_info->host= thd->strdup(tmp->host_or_ip);
if ((thd_info->db=tmp->db)) // Safe test
thd_info->db=thd->strdup(thd_info->db);
thd_info->command=(int) tmp->command;
......
......@@ -23,6 +23,8 @@
#include "sql_acl.h"
#include "sql_select.h"
static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields);
/* Return 0 if row hasn't changed */
static bool compare_record(TABLE *table, ulong query_id)
......@@ -547,11 +549,12 @@ int multi_update::prepare(List<Item> &not_used_values, SELECT_LEX_UNIT *unit)
/*
Store first used table in main_table as this should be updated first
This is because we know that no row in this table will be read twice.
Initialize table for multi table
Create temporary tables to store changed values for all other tables
that are updated.
IMPLEMENTATION
- Update first table in join on the fly, if possible
- Create temporary tables to store changed values for all other tables
that are updated (and main_table if the above doesn't hold).
*/
bool
......@@ -565,53 +568,113 @@ multi_update::initialize_tables(JOIN *join)
main_table=join->join_tab->table;
trans_safe= transactional_tables= main_table->file->has_transactions();
log_delayed= trans_safe || main_table->tmp_table != NO_TMP_TABLE;
table_to_update= (main_table->file->table_flags() & HA_NOT_MULTI_UPDATE) ?
(TABLE *) 0 : main_table;
/* Create a temporary table for all tables after except main table */
table_to_update= 0;
/* Create a temporary table for keys to all tables, except main table */
for (table_ref= update_tables; table_ref; table_ref=table_ref->next)
{
TABLE *table=table_ref->table;
if (table != table_to_update)
{
uint cnt= table_ref->shared;
ORDER group;
List<Item> temp_fields= *fields_for_table[cnt];
TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
/*
Create a temporary table to store all fields that are changed for this
table. The first field in the temporary table is a pointer to the
original row so that we can find and update it
*/
/* ok to be on stack as this is not referenced outside of this func */
Field_string offset(table->file->ref_length, 0, "offset",
table, &my_charset_bin);
if (temp_fields.push_front(new Item_field(((Field *) &offset))))
DBUG_RETURN(1);
uint cnt= table_ref->shared;
List<Item> temp_fields= *fields_for_table[cnt];
ORDER group;
/* Make an unique key over the first field to avoid duplicated updates */
bzero((char*) &group, sizeof(group));
group.asc= 1;
group.item= (Item**) temp_fields.head_ref();
tmp_param->quick_group=1;
tmp_param->field_count=temp_fields.elements;
tmp_param->group_parts=1;
tmp_param->group_length= table->file->ref_length;
if (!(tmp_tables[cnt]=create_tmp_table(thd,
tmp_param,
temp_fields,
(ORDER*) &group, 0, 0,
TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR)))
DBUG_RETURN(1);
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
if (table == main_table) // First table in join
{
if (safe_update_on_fly(join->join_tab, &temp_fields))
{
table_to_update= main_table; // Update table on the fly
continue;
}
}
TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt;
/*
Create a temporary table to store all fields that are changed for this
table. The first field in the temporary table is a pointer to the
original row so that we can find and update it
*/
/* ok to be on stack as this is not referenced outside of this func */
Field_string offset(table->file->ref_length, 0, "offset",
table, 1, &my_charset_bin);
if (temp_fields.push_front(new Item_field(((Field *) &offset))))
DBUG_RETURN(1);
/* Make an unique key over the first field to avoid duplicated updates */
bzero((char*) &group, sizeof(group));
group.asc= 1;
group.item= (Item**) temp_fields.head_ref();
tmp_param->quick_group=1;
tmp_param->field_count=temp_fields.elements;
tmp_param->group_parts=1;
tmp_param->group_length= table->file->ref_length;
if (!(tmp_tables[cnt]=create_tmp_table(thd,
tmp_param,
temp_fields,
(ORDER*) &group, 0, 0,
TMP_TABLE_ALL_COLUMNS,
HA_POS_ERROR)))
DBUG_RETURN(1);
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
}
DBUG_RETURN(0);
}
/*
Check if table is safe to update on fly
SYNOPSIS
safe_update_on_fly
join_tab How table is used in join
fields Fields that are updated
NOTES
We can update the first table in join on the fly if we know that
a row in this tabel will never be read twice. This is true under
the folloing conditions:
- We are doing a table scan and the data is in a separate file (MyISAM) or
if we don't update a clustered key.
- We are doing a range scan and we don't update the scan key or
the primary key for a clustered table handler.
WARNING
This code is a bit dependent of how make_join_readinfo() works.
RETURN
0 Not safe to update
1 Safe to update
*/
static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
{
TABLE *table= join_tab->table;
switch (join_tab->type) {
case JT_SYSTEM:
case JT_CONST:
case JT_EQ_REF:
return 1; // At most one matching row
case JT_REF:
return !check_if_key_used(table, join_tab->ref.key, *fields);
case JT_ALL:
/* If range search on index */
if (join_tab->quick)
return !check_if_key_used(table, join_tab->quick->index,
*fields);
/* If scanning in clustered key */
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->primary_key < MAX_KEY)
return !check_if_key_used(table, table->primary_key, *fields);
return 1;
default:
break; // Avoid compler warning
}
return 0;
}
multi_update::~multi_update()
{
......
......@@ -283,6 +283,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MAX_UPDATES_PER_HOUR
%token MEDIUM_SYM
%token MERGE_SYM
%token MEMORY_SYM
%token MIN_ROWS
%token MYISAM_SYM
%token NAMES_SYM
......@@ -1009,6 +1010,7 @@ table_types:
| MYISAM_SYM { $$= DB_TYPE_MYISAM; }
| MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; }
| HEAP_SYM { $$= DB_TYPE_HEAP; }
| MEMORY_SYM { $$= DB_TYPE_HEAP; }
| BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; }
| INNOBASE_SYM { $$= DB_TYPE_INNODB; };
......@@ -4088,6 +4090,7 @@ keyword:
| MAX_UPDATES_PER_HOUR {}
| MEDIUM_SYM {}
| MERGE_SYM {}
| MEMORY_SYM {}
| MINUTE_SYM {}
| MIN_ROWS {}
| MODIFY_SYM {}
......
......@@ -1245,7 +1245,7 @@ bool check_table_name(const char *name, uint length)
}
}
#endif
if (*name == '/' || *name == FN_LIBCHAR || *name == FN_EXTCHAR)
if (*name == '/' || *name == '\\' || *name == FN_EXTCHAR)
return 1;
name++;
}
......
......@@ -35,6 +35,8 @@
it can be compiled with the UNSIGNED and/or LONGLONG flag set
*/
#define strtoll glob_strtoll /* Fix for True64 */
#include <my_global.h>
#include "m_string.h"
#include "m_ctype.h"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment