diff --git a/Build-tools/Do-compile b/Build-tools/Do-compile
index f3c20c81a9f..e98c3d84937 100755
--- a/Build-tools/Do-compile
+++ b/Build-tools/Do-compile
@@ -10,7 +10,7 @@ use Sys::Hostname;
$opt_distribution=$opt_user=$opt_config_env=$opt_config_extra_env="";
$opt_dbd_options=$opt_perl_options=$opt_config_options=$opt_make_options=$opt_suffix="";
$opt_tmp=$opt_version_suffix="";
-$opt_bundled_zlib=$opt_help=$opt_delete=$opt_debug=$opt_stage=$opt_no_test=$opt_no_perl=$opt_with_low_memory=$opt_fast_benchmark=$opt_static_client=$opt_static_server=$opt_static_perl=$opt_sur=$opt_with_small_disk=$opt_local_perl=$opt_tcpip=$opt_build_thread=$opt_use_old_distribution=$opt_enable_shared=$opt_no_crash_me=$opt_no_strip=$opt_with_cluster=$opt_with_debug=$opt_no_benchmark=$opt_no_mysqltest=$opt_without_embedded=$opt_readline=0;
+$opt_bundled_zlib=$opt_help=$opt_delete=$opt_debug=$opt_stage=$opt_no_test=$opt_no_perl=$opt_one_error=$opt_with_low_memory=$opt_fast_benchmark=$opt_static_client=$opt_static_server=$opt_static_perl=$opt_sur=$opt_with_small_disk=$opt_local_perl=$opt_tcpip=$opt_build_thread=$opt_use_old_distribution=$opt_enable_shared=$opt_no_crash_me=$opt_no_strip=$opt_with_cluster=$opt_with_debug=$opt_no_benchmark=$opt_no_mysqltest=$opt_without_embedded=$opt_readline=0;
$opt_innodb=$opt_bdb=$opt_raid=$opt_libwrap=$opt_clearlogs=0;
GetOptions(
@@ -37,6 +37,7 @@ GetOptions(
"no-test",
"no-mysqltest",
"no-benchmark",
+ "one-error",
"perl-files=s",
"perl-options=s",
"raid",
@@ -310,6 +311,7 @@ if ($opt_stage <= 2)
$command=$make;
$command.= " $opt_make_options" if (defined($opt_make_options) && $opt_make_options ne "");
safe_system($command);
+ print LOG "Do-compile: Build successful\n";
}
#
@@ -372,11 +374,14 @@ $ENV{"LD_LIBRARY_PATH"}= ("$test_dir/lib" .
if ($opt_stage <= 5 && !$opt_no_test && !$opt_no_mysqltest)
{
my $flags= "";
+ my $force= "";
$flags.= " --with-ndbcluster" if ($opt_with_cluster);
+ $flags.= " --force" if (!$opt_one_error);
log_timestamp();
system("mkdir $bench_tmpdir") if (! -d $bench_tmpdir);
safe_cd("${test_dir}/mysql-test");
check_system("./mysql-test-run $flags --tmpdir=$bench_tmpdir --master_port=$mysql_tcp_port --slave_port=$slave_port --ndbcluster_port=$ndbcluster_port --manager-port=$manager_port --no-manager --sleep=10", "tests were successful");
+ # 'mysql-test-run' writes its own final message for log evaluation.
}
#
@@ -555,7 +560,10 @@ Do not run any tests.
Do not run the benchmark test (written in perl)
--no-mysqltest
-Do not run the the mysql-test-run test (Same as 'make test')
+Do not run the mysql-test-run test (Same as 'make test')
+
+--one-error
+Terminate the mysql-test-run test after the first difference (default: use '--force')
--no-perl
Do not compile or install Perl modules, use the system installed ones
diff --git a/client/Makefile.am b/client/Makefile.am
index a78efec1eee..0362c2f3358 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -26,8 +26,8 @@ bin_PROGRAMS = mysql mysqladmin mysqlcheck mysqlshow \
mysqldump mysqlimport mysqltest mysqlbinlog mysqlmanagerc mysqlmanager-pwgen
noinst_HEADERS = sql_string.h completion_hash.h my_readline.h \
client_priv.h
-mysqladmin_SOURCES = mysqladmin.cc
mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc
+mysqladmin_SOURCES = mysqladmin.cc
mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) $(CXXLDFLAGS)
mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS)
mysql_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc
index eec0dcb90fe..8491d0df7b5 100644
--- a/client/mysqladmin.cc
+++ b/client/mysqladmin.cc
@@ -608,7 +608,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
{
char *pos,buff[40];
ulong sec;
- pos=strchr(status,' ');
+ pos= (char*) strchr(status,' ');
*pos++=0;
printf("%s\t\t\t",status); /* print label */
if ((status=str2int(pos,10,0,LONG_MAX,(long*) &sec)))
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index a581453f576..478b46dd246 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -409,4 +409,7 @@
#define ER_PS_MANY_PARAM 1390
#define ER_KEY_PART_0 1391
#define ER_VIEW_CHECKSUM 1392
-#define ER_ERROR_MESSAGES 393
+#define ER_VIEW_MULTIUPDATE 1393
+#define ER_VIEW_NO_INSERT_FIELD_LIST 1394
+#define ER_VIEW_DELETE_MERGE_VIEW 1395
+#define ER_ERROR_MESSAGES 396
diff --git a/innobase/configure.in b/innobase/configure.in
index 652291f1f38..d83da9fdc5c 100644
--- a/innobase/configure.in
+++ b/innobase/configure.in
@@ -110,6 +110,9 @@ esac
case "$target" in
i[[4567]]86-*-*)
CFLAGS="$CFLAGS -DUNIV_INTEL_X86";;
+ # The compiler on Linux/S390 does not seem to have inlining
+ s390-*-*)
+ CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";;
esac
AC_OUTPUT(Makefile os/Makefile ut/Makefile btr/Makefile dnl
diff --git a/myisam/mi_test3.c b/myisam/mi_test3.c
index dca04a9a64b..27d23317b5c 100644
--- a/myisam/mi_test3.c
+++ b/myisam/mi_test3.c
@@ -40,7 +40,7 @@
#endif
-const char *filename= "test3.MSI";
+const char *filename= "test3";
uint tests=10,forks=10,key_cacheing=0,use_log=0;
static void get_options(int argc, char *argv[]);
@@ -363,7 +363,7 @@ int test_write(MI_INFO *file,int id,int lock_type)
}
sprintf(record.id,"%7d",getpid());
- strmov(record.text,"Testing...");
+ strnmov(record.text,"Testing...", sizeof(record.text));
tries=(uint) rnd(100)+10;
for (i=count=0 ; i < tries ; i++)
diff --git a/myisam/mi_write.c b/myisam/mi_write.c
index e059bbb569f..303e924118f 100644
--- a/myisam/mi_write.c
+++ b/myisam/mi_write.c
@@ -165,12 +165,7 @@ err:
{
uint j;
for (j=0 ; j < share->base.keys ; j++)
- {
- if (is_tree_inited(&info->bulk_insert[j]))
- {
- reset_tree(&info->bulk_insert[j]);
- }
- }
+ mi_flush_bulk_insert(info, j);
}
info->errkey= (int) i;
while ( i-- > 0)
@@ -329,7 +324,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
uchar *temp_buff,*keypos;
uchar keybuff[MI_MAX_KEY_BUFF];
my_bool was_last_key;
- my_off_t next_page;
+ my_off_t next_page, dupp_key_pos;
DBUG_ENTER("w_search");
DBUG_PRINT("enter",("page: %ld",page));
@@ -349,9 +344,9 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
/* get position to record with duplicated key */
tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&keypos,keybuff);
if (tmp_key_length)
- info->dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length);
+ dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length);
else
- info->dupp_key_pos= HA_OFFSET_ERROR;
+ dupp_key_pos= HA_OFFSET_ERROR;
if (keyinfo->flag & HA_FULLTEXT)
{
uint off;
@@ -370,7 +365,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
else
{
/* popular word. two-level tree. going down */
- my_off_t root=info->dupp_key_pos;
+ my_off_t root=dupp_key_pos;
keyinfo=&info->s->ft2_keyinfo;
get_key_full_length_rdonly(off, key);
key+=off;
@@ -389,6 +384,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
}
else /* not HA_FULLTEXT, normal HA_NOSAME key */
{
+ info->dupp_key_pos= dupp_key_pos;
my_afree((byte*) temp_buff);
my_errno=HA_ERR_FOUND_DUPP_KEY;
DBUG_RETURN(-1);
diff --git a/mysql-test/r/ctype_uca.result b/mysql-test/r/ctype_uca.result
index 7620b18eea6..cb060ad7ee4 100644
--- a/mysql-test/r/ctype_uca.result
+++ b/mysql-test/r/ctype_uca.result
@@ -19,6 +19,9 @@ select 'a ' = 'a\t', 'a ' < 'a\t', 'a ' > 'a\t';
select 'a a' > 'a', 'a \t' < 'a';
'a a' > 'a' 'a \t' < 'a'
1 1
+select 'c' like '\_' as want0;
+want0
+0
CREATE TABLE t (
c char(20) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result
index 945ec8eae99..599d49208e7 100644
--- a/mysql-test/r/ctype_utf8.result
+++ b/mysql-test/r/ctype_utf8.result
@@ -814,3 +814,6 @@ t2 CREATE TABLE `t2` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t2;
drop table t1;
+select 'c' like '\_' as want0;
+want0
+0
diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result
index 1951f68e822..a85cfd4181c 100644
--- a/mysql-test/r/fulltext.result
+++ b/mysql-test/r/fulltext.result
@@ -399,3 +399,9 @@ select count(*) from t1;
count(*)
1
drop table t1;
+create table t1 (a int primary key, b text, fulltext(b));
+create table t2 (a int, b text);
+insert t1 values (1, "aaaa"), (2, "bbbb");
+insert t2 values (10, "aaaa"), (2, "cccc");
+replace t1 select * from t2;
+drop table t1, t2;
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index cdbed168df2..8f5ac88b7d0 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -363,40 +363,17 @@ NULL test PRIMARY NULL test t1 a 1 NULL NULL NULL
NULL test constraint_1 NULL test t1 a 1 NULL NULL NULL
NULL test key_1 NULL test t1 a 1 NULL NULL NULL
NULL test key_2 NULL test t1 a 1 NULL NULL NULL
-drop table t1;
-CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
-CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id),
-FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
-FOREIGN KEY (t1_id) REFERENCES t1(id) ON UPDATE CASCADE) ENGINE=INNODB;
-select * from information_schema.TABLE_CONSTRAINTS where
-TABLE_SCHEMA= "test";
-CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE CONSTRAINT_METHOD
-NULL test PRIMARY test t1 PRIMARY KEY NULL
-NULL test PRIMARY test t2 PRIMARY KEY NULL
-NULL test t2_ibfk_1 test t2 FOREIGN KEY ON DELETE CASCADE
-NULL test t2_ibfk_2 test t2 FOREIGN KEY ON UPDATE CASCADE
-select * from information_schema.KEY_COLUMN_USAGE where
-TABLE_SCHEMA= "test";
-CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME
-NULL test PRIMARY NULL test t1 id 1 NULL NULL NULL
-NULL test PRIMARY NULL test t2 id 1 NULL NULL NULL
-NULL test t2_ibfk_1 NULL test t2 t1_id 1 NULL id
-NULL test t2_ibfk_2 NULL test t2 t1_id 1 NULL id
select table_name from information_schema.TABLES where table_schema like "test%";
table_name
t1
-t2
select table_name,column_name from information_schema.COLUMNS where table_schema like "test%";
table_name column_name
-t1 id
-t2 id
-t2 t1_id
+t1 a
select ROUTINE_NAME from information_schema.ROUTINES;
ROUTINE_NAME
sel2
sub1
delete from mysql.user where user='mysqltest_1';
-drop table t2;
drop table t1;
drop procedure sel2;
drop function sub1;
diff --git a/mysql-test/r/information_schema_inno.result b/mysql-test/r/information_schema_inno.result
new file mode 100644
index 00000000000..e6dcda2c15d
--- /dev/null
+++ b/mysql-test/r/information_schema_inno.result
@@ -0,0 +1,19 @@
+CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
+CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id),
+FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
+FOREIGN KEY (t1_id) REFERENCES t1(id) ON UPDATE CASCADE) ENGINE=INNODB;
+select * from information_schema.TABLE_CONSTRAINTS where
+TABLE_SCHEMA= "test";
+CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_SCHEMA TABLE_NAME CONSTRAINT_TYPE CONSTRAINT_METHOD
+NULL test PRIMARY test t1 PRIMARY KEY NULL
+NULL test PRIMARY test t2 PRIMARY KEY NULL
+NULL test t2_ibfk_1 test t2 FOREIGN KEY ON DELETE CASCADE
+NULL test t2_ibfk_2 test t2 FOREIGN KEY ON UPDATE CASCADE
+select * from information_schema.KEY_COLUMN_USAGE where
+TABLE_SCHEMA= "test";
+CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION REFERENCED_TABLE_SCHEMA REFERENCED_TABLE_NAME REFERENCED_COLUMN_NAME
+NULL test PRIMARY NULL test t1 id 1 NULL NULL NULL
+NULL test PRIMARY NULL test t2 id 1 NULL NULL NULL
+NULL test t2_ibfk_1 NULL test t2 t1_id 1 NULL id
+NULL test t2_ibfk_2 NULL test t2 t1_id 1 NULL id
+drop table t2, t1;
diff --git a/mysql-test/r/lowercase_table3.result b/mysql-test/r/lowercase_table3.result
index a645e46be9e..8182d07c26b 100644
--- a/mysql-test/r/lowercase_table3.result
+++ b/mysql-test/r/lowercase_table3.result
@@ -6,5 +6,5 @@ drop table t1;
flush tables;
CREATE TABLE t1 (a int) ENGINE=INNODB;
SELECT * from T1;
-ERROR HY000: Can't open file: 'T1.InnoDB' (errno: 1)
+ERROR HY000: Can't open file: 'T1.ibd' (errno: 1)
drop table t1;
diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result
index 64b40389690..0644b32015c 100644
--- a/mysql-test/r/lowercase_view.result
+++ b/mysql-test/r/lowercase_view.result
@@ -22,3 +22,19 @@ insert into v2aA values ((select max(col1) from v1aA));
ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause
drop view v2Aa,v1Aa;
drop table t1Aa,t2Aa;
+create table t1Aa (col1 int);
+create view v1Aa as select col1 from t1Aa as AaA;
+show create view v1AA;
+View Create View
+v1aa CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1aa` AS select `aaa`.`col1` AS `col1` from `test`.`t1aa` `AaA`
+drop view v1AA;
+select Aaa.col1 from t1Aa as AaA;
+col1
+create view v1Aa as select Aaa.col1 from t1Aa as AaA;
+drop view v1AA;
+create view v1Aa as select AaA.col1 from t1Aa as AaA;
+show create view v1AA;
+View Create View
+v1aa CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1aa` AS select `aaa`.`col1` AS `col1` from `test`.`t1aa` `AaA`
+drop view v1AA;
+drop table t1Aa;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index f7aaf2c9ee4..e2c6b3f2ae2 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -887,7 +887,7 @@ insert into t1 values (1), (2), (3), (200);
create view v1 (x) as select a from t1 where a > 1;
create view v2 (y) as select x from v1 where x < 100;
select * from v2;
-x
+y
2
3
drop table t1;
@@ -1661,3 +1661,171 @@ check table v1;
Table Op Msg_type Msg_text
test.v1 check error View 'test.v1' references invalid table(s) or column(s)
drop view v1;
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+insert into t1 values (1), (2), (3);
+insert into t2 values (1), (3);
+insert into t3 values (1), (2), (4);
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1 left join t2 on (t1.a=t2.a);
+select * from t3 left join v3 on (t3.a = v3.a);
+a a b
+1 1 1
+2 2 NULL
+4 NULL NULL
+explain extended select * from t3 left join v3 on (t3.a = v3.a);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 ALL NULL NULL NULL NULL 3
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `b` from `test`.`t3` left join (`test`.`t1` left join `test`.`t2` on((`test`.`t1`.`a` = `test`.`t2`.`a`))) on((`test`.`t3`.`a` = `test`.`t1`.`a`)) where 1
+create view v1 (a) as select a from t1;
+create view v2 (a) as select a from t2;
+create view v4 (a,b) as select v1.a as a, v2.a as b from v1 left join v2 on (v1.a=v2.a);
+select * from t3 left join v4 on (t3.a = v4.a);
+a a b
+1 1 1
+2 2 NULL
+4 NULL NULL
+explain extended select * from t3 left join v4 on (t3.a = v4.a);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 ALL NULL NULL NULL NULL 3
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t3`.`a` AS `a`,`test`.`t1`.`a` AS `a`,`test`.`t2`.`a` AS `b` from `test`.`t3` left join (`test`.`v1` left join `test`.`v2` on((`test`.`t1`.`a` = `test`.`t2`.`a`))) on((`test`.`t3`.`a` = `test`.`t1`.`a`)) where 1
+prepare stmt1 from "select * from t3 left join v4 on (t3.a = v4.a);";
+execute stmt1;
+a a b
+1 1 1
+2 2 NULL
+4 NULL NULL
+execute stmt1;
+a a b
+1 1 1
+2 2 NULL
+4 NULL NULL
+deallocate prepare stmt1;
+drop view v4,v3,v2,v1;
+drop tables t1,t2,t3;
+create table t1 (a int, primary key (a), b int);
+create table t2 (a int, primary key (a));
+insert into t1 values (1,100), (2,200);
+insert into t2 values (1), (3);
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
+update v3 set a= 10 where a=1;
+select * from t1;
+a b
+10 100
+2 200
+select * from t2;
+a
+1
+3
+create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
+set updatable_views_with_limit=NO;
+update v2 set a= 10 where a=200 limit 1;
+ERROR HY000: The target table t1 of the UPDATE is not updatable
+set updatable_views_with_limit=DEFAULT;
+select * from v3;
+a b
+2 1
+10 1
+2 3
+10 3
+select * from v2;
+a b
+100 1
+200 1
+100 3
+200 3
+set @a= 10;
+set @b= 100;
+prepare stmt1 from "update v3 set a= ? where a=?";
+execute stmt1 using @a,@b;
+select * from v3;
+a b
+2 1
+10 1
+2 3
+10 3
+set @a= 300;
+set @b= 10;
+execute stmt1 using @a,@b;
+select * from v3;
+a b
+2 1
+300 1
+2 3
+300 3
+deallocate prepare stmt1;
+drop view v3,v2;
+drop tables t1,t2;
+create table t1 (a int, primary key (a), b int);
+create table t2 (a int, primary key (a), b int);
+insert into t2 values (1000, 2000);
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
+insert into v3 values (1,2);
+ERROR HY000: Can not insert into join view 'test.v3' without fields list
+insert into v3 select * from t2;
+ERROR HY000: Can not insert into join view 'test.v3' without fields list
+insert into v3(a,b) values (1,2);
+ERROR HY000: Can not modify more than one base table through a join view 'test.v3'
+insert into v3(a,b) select * from t2;
+ERROR HY000: Can not modify more than one base table through a join view 'test.v3'
+insert into v3(a) values (1);
+insert into v3(b) values (10);
+insert into v3(a) select a from t2;
+insert into v3(b) select b from t2;
+Warnings:
+Warning 1263 Column set to default value; NULL supplied to NOT NULL column 'a' at row 2
+insert into v3(a) values (1) on duplicate key update a=a+10000+VALUES(a);
+select * from t1;
+a b
+10002 NULL
+10 NULL
+1000 NULL
+select * from t2;
+a b
+1000 2000
+10 NULL
+2000 NULL
+0 NULL
+delete from v3;
+ERROR HY000: Can not delete from join view 'test.v3'
+delete v3,t1 from v3,t1;
+ERROR HY000: Can not delete from join view 'test.v3'
+delete from t1;
+prepare stmt1 from "insert into v3(a) values (?);";
+set @a= 100;
+execute stmt1 using @a;
+set @a= 300;
+execute stmt1 using @a;
+deallocate prepare stmt1;
+prepare stmt1 from "insert into v3(a) select ?;";
+set @a= 101;
+execute stmt1 using @a;
+set @a= 301;
+execute stmt1 using @a;
+deallocate prepare stmt1;
+select * from v3;
+a b
+100 1000
+101 1000
+300 1000
+301 1000
+100 10
+101 10
+300 10
+301 10
+100 2000
+101 2000
+300 2000
+301 2000
+100 0
+101 0
+300 0
+301 0
+drop view v3;
+drop tables t1,t2;
diff --git a/mysql-test/t/ctype_uca.test b/mysql-test/t/ctype_uca.test
index e640e6b53dc..11833ba9bc7 100644
--- a/mysql-test/t/ctype_uca.test
+++ b/mysql-test/t/ctype_uca.test
@@ -24,6 +24,11 @@ select 'a ' = 'a\t', 'a ' < 'a\t', 'a ' > 'a\t';
select 'a a' > 'a', 'a \t' < 'a';
+#
+# Bug #6787 LIKE not working properly with _ and utf8 data
+#
+select 'c' like '\_' as want0;
+
#
# Bug #5679 utf8_unicode_ci LIKE--trailing % doesn't equal zero characters
#
diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test
index c75b1dee63c..42031be8f3c 100644
--- a/mysql-test/t/ctype_utf8.test
+++ b/mysql-test/t/ctype_utf8.test
@@ -660,3 +660,9 @@ create table t2 select concat(a,_utf8'') as a, concat(b,_utf8'')as b from t1;
show create table t2;
drop table t2;
drop table t1;
+
+#
+# Bug #6787 LIKE not working properly with _ and utf8 data
+#
+select 'c' like '\_' as want0;
+
diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test
index 41fbf3f27ac..008e965297f 100644
--- a/mysql-test/t/fulltext.test
+++ b/mysql-test/t/fulltext.test
@@ -308,3 +308,16 @@ REPAIR TABLE t1;
select count(*) from t1;
drop table t1;
+#
+# bug#6784
+# mi_flush_bulk_insert (on dup key error in mi_write)
+# was mangling info->dupp_key_pos
+#
+
+create table t1 (a int primary key, b text, fulltext(b));
+create table t2 (a int, b text);
+insert t1 values (1, "aaaa"), (2, "bbbb");
+insert t2 values (10, "aaaa"), (2, "cccc");
+replace t1 select * from t2;
+drop table t1, t2;
+
diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test
index b52a3c2edec..411301445a3 100644
--- a/mysql-test/t/information_schema.test
+++ b/mysql-test/t/information_schema.test
@@ -162,16 +162,7 @@ select * from information_schema.TABLE_CONSTRAINTS where
TABLE_SCHEMA= "test";
select * from information_schema.KEY_COLUMN_USAGE where
TABLE_SCHEMA= "test";
-drop table t1;
-CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
-CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id),
-FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
-FOREIGN KEY (t1_id) REFERENCES t1(id) ON UPDATE CASCADE) ENGINE=INNODB;
-select * from information_schema.TABLE_CONSTRAINTS where
-TABLE_SCHEMA= "test";
-select * from information_schema.KEY_COLUMN_USAGE where
-TABLE_SCHEMA= "test";
connect (user1,localhost,mysqltest_1,,);
connection user1;
@@ -181,7 +172,6 @@ select ROUTINE_NAME from information_schema.ROUTINES;
disconnect user1;
connection default;
delete from mysql.user where user='mysqltest_1';
-drop table t2;
drop table t1;
drop procedure sel2;
drop function sub1;
diff --git a/mysql-test/t/information_schema_inno.test b/mysql-test/t/information_schema_inno.test
new file mode 100644
index 00000000000..15643ebe90c
--- /dev/null
+++ b/mysql-test/t/information_schema_inno.test
@@ -0,0 +1,16 @@
+-- source include/have_innodb.inc
+
+#
+# Test for KEY_COLUMN_USAGE & TABLE_CONSTRAINTS tables
+#
+
+CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
+CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id),
+FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
+FOREIGN KEY (t1_id) REFERENCES t1(id) ON UPDATE CASCADE) ENGINE=INNODB;
+select * from information_schema.TABLE_CONSTRAINTS where
+TABLE_SCHEMA= "test";
+select * from information_schema.KEY_COLUMN_USAGE where
+TABLE_SCHEMA= "test";
+
+drop table t2, t1;
diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test
index 2a2757650ae..4b688cfb922 100644
--- a/mysql-test/t/lowercase_view.test
+++ b/mysql-test/t/lowercase_view.test
@@ -32,3 +32,18 @@ delete from v2aA where col1 = (select max(col1) from v1aA);
insert into v2aA values ((select max(col1) from v1aA));
drop view v2Aa,v1Aa;
drop table t1Aa,t2Aa;
+
+#
+# aliases in VIEWs
+#
+create table t1Aa (col1 int);
+create view v1Aa as select col1 from t1Aa as AaA;
+show create view v1AA;
+drop view v1AA;
+select Aaa.col1 from t1Aa as AaA;
+create view v1Aa as select Aaa.col1 from t1Aa as AaA;
+drop view v1AA;
+create view v1Aa as select AaA.col1 from t1Aa as AaA;
+show create view v1AA;
+drop view v1AA;
+drop table t1Aa;
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index 1b74adc583b..92747323336 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -1599,3 +1599,115 @@ check table v1,t1;
drop table t1;
check table v1;
drop view v1;
+
+#
+# merge of VIEW with several tables
+#
+create table t1 (a int);
+create table t2 (a int);
+create table t3 (a int);
+insert into t1 values (1), (2), (3);
+insert into t2 values (1), (3);
+insert into t3 values (1), (2), (4);
+# view over tables
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1 left join t2 on (t1.a=t2.a);
+select * from t3 left join v3 on (t3.a = v3.a);
+explain extended select * from t3 left join v3 on (t3.a = v3.a);
+# view over views
+create view v1 (a) as select a from t1;
+create view v2 (a) as select a from t2;
+create view v4 (a,b) as select v1.a as a, v2.a as b from v1 left join v2 on (v1.a=v2.a);
+select * from t3 left join v4 on (t3.a = v4.a);
+explain extended select * from t3 left join v4 on (t3.a = v4.a);
+# PS with view over views
+prepare stmt1 from "select * from t3 left join v4 on (t3.a = v4.a);";
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+drop view v4,v3,v2,v1;
+drop tables t1,t2,t3;
+
+#
+# updating of join view
+#
+create table t1 (a int, primary key (a), b int);
+create table t2 (a int, primary key (a));
+insert into t1 values (1,100), (2,200);
+insert into t2 values (1), (3);
+# legal view for update
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
+update v3 set a= 10 where a=1;
+select * from t1;
+select * from t2;
+# view without primary key
+create view v2 (a,b) as select t1.b as a, t2.a as b from t1, t2;
+set updatable_views_with_limit=NO;
+-- error 1288
+update v2 set a= 10 where a=200 limit 1;
+set updatable_views_with_limit=DEFAULT;
+# just view selects
+select * from v3;
+select * from v2;
+# prepare statement with updating join view
+set @a= 10;
+set @b= 100;
+prepare stmt1 from "update v3 set a= ? where a=?";
+execute stmt1 using @a,@b;
+select * from v3;
+set @a= 300;
+set @b= 10;
+execute stmt1 using @a,@b;
+select * from v3;
+deallocate prepare stmt1;
+drop view v3,v2;
+drop tables t1,t2;
+
+#
+# inserting/deleting join view
+#
+create table t1 (a int, primary key (a), b int);
+create table t2 (a int, primary key (a), b int);
+insert into t2 values (1000, 2000);
+create view v3 (a,b) as select t1.a as a, t2.a as b from t1, t2;
+# inserting into join view without field list
+-- error 1394
+insert into v3 values (1,2);
+-- error 1394
+insert into v3 select * from t2;
+# inserting in several tables of join view
+-- error 1393
+insert into v3(a,b) values (1,2);
+-- error 1393
+insert into v3(a,b) select * from t2;
+# correct inserts into join view
+insert into v3(a) values (1);
+insert into v3(b) values (10);
+insert into v3(a) select a from t2;
+insert into v3(b) select b from t2;
+insert into v3(a) values (1) on duplicate key update a=a+10000+VALUES(a);
+select * from t1;
+select * from t2;
+# try delete from join view
+-- error 1395
+delete from v3;
+-- error 1395
+delete v3,t1 from v3,t1;
+# delete from t1 just to reduce result set size
+delete from t1;
+# prepare statement with insert join view
+prepare stmt1 from "insert into v3(a) values (?);";
+set @a= 100;
+execute stmt1 using @a;
+set @a= 300;
+execute stmt1 using @a;
+deallocate prepare stmt1;
+prepare stmt1 from "insert into v3(a) select ?;";
+set @a= 101;
+execute stmt1 using @a;
+set @a= 301;
+execute stmt1 using @a;
+deallocate prepare stmt1;
+select * from v3;
+
+drop view v3;
+drop tables t1,t2;
diff --git a/ndb/docs/wl2077.txt b/ndb/docs/wl2077.txt
new file mode 100644
index 00000000000..5a77c18aa2a
--- /dev/null
+++ b/ndb/docs/wl2077.txt
@@ -0,0 +1,35 @@
+
+100' * (select 1 from T1 (1M rows) where key = rand());
+1 host, 1 ndbd, api co-hosted
+results in 1000 rows / sec
+
+ wo/reset bounds w/ rb
+4.1-read committed a) 4.9 b) 7.4
+4.1-read hold lock c) 4.7 d) 6.7
+
+wl2077-read committed 6.4 (+30%) 10.8 (+45%)
+wl2077-read hold lock 4.6 (-1%) 6.7 (+ 0%)
+
+-- Comparision e)
+serial pk: 10.9'
+batched (1000): 59'
+serial uniq index: 8.4'
+batched (1000): 33'
+index range (1000): 186'
+
+----
+
+load) testScanPerf -c 1 -d 1 T1
+a) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 0 -r 2 -q 0 T1
+b) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 0 -r 2 -q 1 T1
+c) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 1 -r 2 -q 0 T1
+d) testScanPerf -s 100000 -c 0 -d 0 -a 1 -l 1 -r 2 -q 1 T1
+e) testReadPerf -i 25 -c 0 -d 0 T1
+
+--- music join 1db-co 2db-co
+
+4.1 13s 14s
+4.1 wo/ blobs 1.7s 3.2s
+
+wl2077 12s 14s
+wl2077 wo/ blobs 1.2s (-30%) 2.5s (-22%)
diff --git a/ndb/include/Makefile.am b/ndb/include/Makefile.am
index ca2e8152352..61f55cf9d61 100644
--- a/ndb/include/Makefile.am
+++ b/ndb/include/Makefile.am
@@ -28,15 +28,14 @@ ndbapi/NdbIndexScanOperation.hpp \
ndbapi/ndberror.h
mgmapiinclude_HEADERS = \
-mgmapi/LocalConfig.hpp \
mgmapi/mgmapi.h \
-mgmapi/mgmapi_debug.h
+mgmapi/mgmapi_debug.h \
+mgmapi/mgmapi_config_parameters.h \
+mgmapi/mgmapi_config_parameters_debug.h
noinst_HEADERS = \
ndb_global.h \
-ndb_net.h \
-mgmapi/mgmapi_config_parameters.h \
-mgmapi/mgmapi_config_parameters_debug.h
+ndb_net.h
EXTRA_DIST = debugger editline kernel logger mgmcommon \
portlib transporter util
diff --git a/ndb/include/mgmapi/mgmapi.h b/ndb/include/mgmapi/mgmapi.h
index f1ef357421b..a23417f153a 100644
--- a/ndb/include/mgmapi/mgmapi.h
+++ b/ndb/include/mgmapi/mgmapi.h
@@ -356,11 +356,26 @@ extern "C" {
/**
* Create a handle to a management server
*
- * @return A management handle
- * or NULL if no management handle could be created.
+ * @return A management handle
+ * or NULL if no management handle could be created.
*/
NdbMgmHandle ndb_mgm_create_handle();
+ /**
+ * Set connecst string to management server
+ *
+ * @param handle Management handle
+ * @param connect_string Connect string to the management server,
+ *
+ * @return -1 on error.
+ */
+ int ndb_mgm_set_connectstring(NdbMgmHandle handle,
+ const char *connect_string);
+
+ int ndb_mgm_get_configuration_nodeid(NdbMgmHandle handle);
+ int ndb_mgm_get_connected_port(NdbMgmHandle handle);
+ const char *ndb_mgm_get_connected_host(NdbMgmHandle handle);
+
/**
* Destroy a management server handle
*
@@ -378,11 +393,10 @@ extern "C" {
* Connect to a management server
*
* @param handle Management handle.
- * @param mgmsrv Hostname and port of the management server,
- * "hostname:port".
* @return -1 on error.
*/
- int ndb_mgm_connect(NdbMgmHandle handle, const char * mgmsrv);
+ int ndb_mgm_connect(NdbMgmHandle handle, int no_retries,
+ int retry_delay_in_seconds, int verbose);
/**
* Disconnect from a management server
@@ -709,9 +723,7 @@ extern "C" {
void ndb_mgm_destroy_configuration(struct ndb_mgm_configuration *);
int ndb_mgm_alloc_nodeid(NdbMgmHandle handle,
- unsigned version,
- unsigned *pnodeid,
- int nodetype);
+ unsigned version, int nodetype);
/**
* Config iterator
*/
diff --git a/ndb/include/mgmcommon/ConfigRetriever.hpp b/ndb/include/mgmcommon/ConfigRetriever.hpp
index 6c32255e921..80449628867 100644
--- a/ndb/include/mgmcommon/ConfigRetriever.hpp
+++ b/ndb/include/mgmcommon/ConfigRetriever.hpp
@@ -20,7 +20,6 @@
#include
#include
#include
-#include
/**
* @class ConfigRetriever
@@ -28,10 +27,11 @@
*/
class ConfigRetriever {
public:
- ConfigRetriever(LocalConfig &local_config, Uint32 version, Uint32 nodeType);
+ ConfigRetriever(const char * _connect_string,
+ Uint32 version, Uint32 nodeType);
~ConfigRetriever();
- int do_connect(int exit_on_connect_failure= false);
+ int do_connect(int no_retries, int retry_delay_in_seconds, int verbose);
/**
* Get configuration for current node.
@@ -46,12 +46,14 @@ public:
*/
struct ndb_mgm_configuration * getConfig();
+ void resetError();
+ int hasError();
const char * getErrorString();
/**
* @return Node id of this node (as stated in local config or connectString)
*/
- Uint32 allocNodeId();
+ Uint32 allocNodeId(int no_retries, int retry_delay_in_seconds);
/**
* Get config using socket
@@ -68,22 +70,26 @@ public:
*/
bool verifyConfig(const struct ndb_mgm_configuration *, Uint32 nodeid);
- Uint32 get_mgmd_port() const {return m_mgmd_port;};
- const char *get_mgmd_host() const {return m_mgmd_host;};
+ Uint32 get_mgmd_port() const;
+ const char *get_mgmd_host() const;
+
+ Uint32 get_configuration_nodeid() const;
private:
BaseString errorString;
enum ErrorType {
- CR_ERROR = 0,
- CR_RETRY = 1
+ CR_NO_ERROR = 0,
+ CR_ERROR = 1,
+ CR_RETRY = 2
};
ErrorType latestErrorType;
void setError(ErrorType, const char * errorMsg);
- struct LocalConfig& _localConfig;
- Uint32 _ownNodeId;
+ Uint32 _ownNodeId;
+ /*
Uint32 m_mgmd_port;
const char *m_mgmd_host;
+ */
Uint32 m_version;
Uint32 m_node_type;
diff --git a/ndb/include/ndbapi/NdbIndexScanOperation.hpp b/ndb/include/ndbapi/NdbIndexScanOperation.hpp
index 66b3fc9d43b..a3388f62f58 100644
--- a/ndb/include/ndbapi/NdbIndexScanOperation.hpp
+++ b/ndb/include/ndbapi/NdbIndexScanOperation.hpp
@@ -113,7 +113,7 @@ public:
* Reset bounds and put operation in list that will be
* sent on next execute
*/
- int reset_bounds();
+ int reset_bounds(bool forceSend = false);
bool getSorted() const { return m_ordered; }
private:
@@ -127,8 +127,8 @@ private:
virtual NdbRecAttr* getValue_impl(const NdbColumnImpl*, char*);
void fix_get_values();
- int next_result_ordered(bool fetchAllowed);
- int send_next_scan_ordered(Uint32 idx);
+ int next_result_ordered(bool fetchAllowed, bool forceSend = false);
+ int send_next_scan_ordered(Uint32 idx, bool forceSend = false);
int compare(Uint32 key, Uint32 cols, const NdbReceiver*, const NdbReceiver*);
Uint32 m_sort_columns;
diff --git a/ndb/include/ndbapi/NdbResultSet.hpp b/ndb/include/ndbapi/NdbResultSet.hpp
index 478daf8aad2..dc0288a380c 100644
--- a/ndb/include/ndbapi/NdbResultSet.hpp
+++ b/ndb/include/ndbapi/NdbResultSet.hpp
@@ -89,17 +89,17 @@ public:
* - 1: if there are no more tuples to scan.
* - 2: if there are no more cached records in NdbApi
*/
- int nextResult(bool fetchAllowed = true);
+ int nextResult(bool fetchAllowed = true, bool forceSend = false);
/**
* Close result set (scan)
*/
- void close();
+ void close(bool forceSend = false);
/**
* Restart
*/
- int restart();
+ int restart(bool forceSend = false);
/**
* Transfer scan operation to an updating transaction. Use this function
diff --git a/ndb/include/ndbapi/NdbScanOperation.hpp b/ndb/include/ndbapi/NdbScanOperation.hpp
index 2e4d173ac75..3c95c79e776 100644
--- a/ndb/include/ndbapi/NdbScanOperation.hpp
+++ b/ndb/include/ndbapi/NdbScanOperation.hpp
@@ -90,11 +90,11 @@ protected:
NdbScanOperation(Ndb* aNdb);
virtual ~NdbScanOperation();
- int nextResult(bool fetchAllowed = true);
+ int nextResult(bool fetchAllowed = true, bool forceSend = false);
virtual void release();
- void closeScan();
- int close_impl(class TransporterFacade*);
+ void closeScan(bool forceSend = false);
+ int close_impl(class TransporterFacade*, bool forceSend = false);
// Overloaded methods from NdbCursorOperation
int executeCursor(int ProcessorId);
@@ -103,6 +103,7 @@ protected:
int init(const NdbTableImpl* tab, NdbConnection* myConnection);
int prepareSend(Uint32 TC_ConnectPtr, Uint64 TransactionId);
int doSend(int ProcessorId);
+ void checkForceSend(bool forceSend);
virtual void setErrorCode(int aErrorCode);
virtual void setErrorCodeAbort(int aErrorCode);
@@ -138,7 +139,7 @@ protected:
Uint32 m_sent_receivers_count; // NOTE needs mutex to access
NdbReceiver** m_sent_receivers; // receive thread puts them here
- int send_next_scan(Uint32 cnt, bool close);
+ int send_next_scan(Uint32 cnt, bool close, bool forceSend = false);
void receiver_delivered(NdbReceiver*);
void receiver_completed(NdbReceiver*);
void execCLOSE_SCAN_REP();
@@ -148,7 +149,7 @@ protected:
Uint32 m_ordered;
- int restart();
+ int restart(bool forceSend = false);
};
inline
diff --git a/ndb/include/ndbapi/ndb_cluster_connection.hpp b/ndb/include/ndbapi/ndb_cluster_connection.hpp
index f8e6f25ce73..59d5a038844 100644
--- a/ndb/include/ndbapi/ndb_cluster_connection.hpp
+++ b/ndb/include/ndbapi/ndb_cluster_connection.hpp
@@ -19,7 +19,6 @@
#define CLUSTER_CONNECTION_HPP
class TransporterFacade;
-class LocalConfig;
class ConfigRetriever;
class NdbThread;
@@ -38,7 +37,6 @@ private:
void connect_thread();
char *m_connect_string;
TransporterFacade *m_facade;
- LocalConfig *m_local_config;
ConfigRetriever *m_config_retriever;
NdbThread *m_connect_thread;
int (*m_connect_callback)(void);
diff --git a/ndb/include/util/ndb_opts.h b/ndb/include/util/ndb_opts.h
index 6cba9c04449..f7ae3b5489e 100644
--- a/ndb/include/util/ndb_opts.h
+++ b/ndb/include/util/ndb_opts.h
@@ -32,10 +32,13 @@
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, \
{ "version", 'V', "Output version information and exit.", 0, 0, 0, \
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, \
- { "connect-string", 'c', \
+ { "ndb-connectstring", 'c', \
"Set connect string for connecting to ndb_mgmd. " \
- "=\"host=[;nodeid=]\". " \
- "Overides specifying entries in NDB_CONNECTSTRING and config file", \
+ "Syntax: \"[nodeid=;][host=][:]\". " \
+ "Overides specifying entries in NDB_CONNECTSTRING and Ndb.cfg", \
+ (gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0, \
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },\
+ { "connect-string", 'c', "same as --ndb-connectstring",\
(gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0, \
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }
#else
@@ -46,11 +49,14 @@
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, \
{ "version", 'V', "Output version information and exit.", 0, 0, 0, \
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, \
- { "connect-string", 'c', \
+ { "ndb-connectstring", 'c', \
"Set connect string for connecting to ndb_mgmd. " \
- "=\"host=[;nodeid=]\". " \
- "Overides specifying entries in NDB_CONNECTSTRING and config file", \
+ "Syntax: \"[nodeid=;][host=][:]\". " \
+ "Overides specifying entries in NDB_CONNECTSTRING and Ndb.cfg", \
(gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0, \
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },\
+ { "connect-string", 'c', "same as --ndb-connectstring",\
+ (gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0,\
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }
#endif
diff --git a/ndb/src/common/debugger/signaldata/ScanTab.cpp b/ndb/src/common/debugger/signaldata/ScanTab.cpp
index 72a4d9f94b9..0755ee0a856 100644
--- a/ndb/src/common/debugger/signaldata/ScanTab.cpp
+++ b/ndb/src/common/debugger/signaldata/ScanTab.cpp
@@ -30,13 +30,14 @@ printSCANTABREQ(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiv
fprintf(output, " apiConnectPtr: H\'%.8x",
sig->apiConnectPtr);
fprintf(output, " requestInfo: H\'%.8x:\n", requestInfo);
- fprintf(output, " Parallellism: %u, Batch: %u LockMode: %u, Keyinfo: %u Holdlock: %u, RangeScan: %u\n",
+ fprintf(output, " Parallellism: %u, Batch: %u LockMode: %u, Keyinfo: %u Holdlock: %u, RangeScan: %u ReadCommitted: %u\n",
sig->getParallelism(requestInfo),
sig->getScanBatch(requestInfo),
sig->getLockMode(requestInfo),
+ sig->getKeyinfoFlag(requestInfo),
sig->getHoldLockFlag(requestInfo),
sig->getRangeScanFlag(requestInfo),
- sig->getKeyinfoFlag(requestInfo));
+ sig->getReadCommittedFlag(requestInfo));
Uint32 keyLen = (sig->attrLenKeyLen >> 16);
Uint32 attrLen = (sig->attrLenKeyLen & 0xFFFF);
diff --git a/ndb/src/common/logger/LogHandler.cpp b/ndb/src/common/logger/LogHandler.cpp
index 4fab957fc50..a76cb622878 100644
--- a/ndb/src/common/logger/LogHandler.cpp
+++ b/ndb/src/common/logger/LogHandler.cpp
@@ -117,10 +117,9 @@ LogHandler::parseParams(const BaseString &_params) {
_params.split(v_args, ",");
for(size_t i=0; i < v_args.size(); i++) {
Vector v_param_value;
-
- v_args[i].split(v_param_value, "=", 2);
- if(v_param_value.size() == 2 &&
- !setParam(v_param_value[0], v_param_value[1]))
+ if(v_args[i].split(v_param_value, "=", 2) != 2)
+ ret = false;
+ else if (!setParam(v_param_value[0], v_param_value[1]))
ret = false;
}
diff --git a/ndb/src/common/logger/Logger.cpp b/ndb/src/common/logger/Logger.cpp
index 00a2fae67bc..1dc3bd43716 100644
--- a/ndb/src/common/logger/Logger.cpp
+++ b/ndb/src/common/logger/Logger.cpp
@@ -169,10 +169,13 @@ Logger::addHandler(const BaseString &logstring) {
size_t i;
Vector logdest;
Vectorloghandlers;
+ DBUG_ENTER("Logger::addHandler");
logstring.split(logdest, ";");
for(i = 0; i < logdest.size(); i++) {
+ DBUG_PRINT("info",("adding: %s",logdest[i]));
+
Vector v_type_args;
logdest[i].split(v_type_args, ":", 2);
@@ -191,16 +194,16 @@ Logger::addHandler(const BaseString &logstring) {
handler = new ConsoleLogHandler();
if(handler == NULL)
- return false;
+ DBUG_RETURN(false);
if(!handler->parseParams(params))
- return false;
+ DBUG_RETURN(false);
loghandlers.push_back(handler);
}
for(i = 0; i < loghandlers.size(); i++)
addHandler(loghandlers[i]);
- return true; /* @todo handle errors */
+ DBUG_RETURN(true); /* @todo handle errors */
}
bool
diff --git a/ndb/src/common/mgmcommon/ConfigRetriever.cpp b/ndb/src/common/mgmcommon/ConfigRetriever.cpp
index a1b979f62d8..0af5eb2f83c 100644
--- a/ndb/src/common/mgmcommon/ConfigRetriever.cpp
+++ b/ndb/src/common/mgmcommon/ConfigRetriever.cpp
@@ -20,7 +20,6 @@
#include
#include
-#include "LocalConfig.hpp"
#include
#include
@@ -45,90 +44,62 @@
//****************************************************************************
//****************************************************************************
-ConfigRetriever::ConfigRetriever(LocalConfig &local_config,
+ConfigRetriever::ConfigRetriever(const char * _connect_string,
Uint32 version, Uint32 node_type)
- : _localConfig(local_config)
{
- m_handle= 0;
m_version = version;
m_node_type = node_type;
- _ownNodeId = _localConfig._ownNodeId;
+ _ownNodeId= 0;
+
+ m_handle= ndb_mgm_create_handle();
+
+ if (m_handle == 0) {
+ setError(CR_ERROR, "Unable to allocate mgm handle");
+ return;
+ }
+
+ if (ndb_mgm_set_connectstring(m_handle, _connect_string))
+ {
+ setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
+ return;
+ }
+ resetError();
}
-ConfigRetriever::~ConfigRetriever(){
-
+ConfigRetriever::~ConfigRetriever()
+{
if (m_handle) {
ndb_mgm_disconnect(m_handle);
ndb_mgm_destroy_handle(&m_handle);
}
}
+Uint32
+ConfigRetriever::get_configuration_nodeid() const
+{
+ return ndb_mgm_get_configuration_nodeid(m_handle);
+}
+
+Uint32 ConfigRetriever::get_mgmd_port() const
+{
+ return ndb_mgm_get_connected_port(m_handle);
+}
+
+const char *ConfigRetriever::get_mgmd_host() const
+{
+ return ndb_mgm_get_connected_host(m_handle);
+}
//****************************************************************************
//****************************************************************************
int
-ConfigRetriever::do_connect(int exit_on_connect_failure){
-
- m_mgmd_port= 0;
- m_mgmd_host= 0;
-
- if(!m_handle)
- m_handle= ndb_mgm_create_handle();
-
- if (m_handle == 0) {
- setError(CR_ERROR, "Unable to allocate mgm handle");
- return -1;
- }
-
- int retry = 1;
- int retry_max = 12; // Max number of retry attempts
- int retry_interval= 5; // Seconds between each retry
- while(retry < retry_max){
- Uint32 type = CR_ERROR;
- BaseString tmp;
- for (unsigned int i = 0; i<_localConfig.ids.size(); i++){
- MgmtSrvrId * m = &_localConfig.ids[i];
- DBUG_PRINT("info",("trying %s:%d",
- m->name.c_str(),
- m->port));
- switch(m->type){
- case MgmId_TCP:
- tmp.assfmt("%s:%d", m->name.c_str(), m->port);
- if (ndb_mgm_connect(m_handle, tmp.c_str()) == 0) {
- m_mgmd_port= m->port;
- m_mgmd_host= m->name.c_str();
- DBUG_PRINT("info",("connected to ndb_mgmd at %s:%d",
- m_mgmd_host,
- m_mgmd_port));
- return 0;
- }
- setError(CR_RETRY, ndb_mgm_get_latest_error_desc(m_handle));
- case MgmId_File:
- break;
- }
- }
- if(latestErrorType == CR_RETRY){
- DBUG_PRINT("info",("CR_RETRY"));
- if (exit_on_connect_failure)
- return 1;
- REPORT_WARNING("Failed to retrieve cluster configuration");
- ndbout << "(Cause of failure: " << getErrorString() << ")" << endl;
- ndbout << "Attempt " << retry << " of " << retry_max << ". "
- << "Trying again in "<< retry_interval <<" seconds..."
- << endl << endl;
- NdbSleep_SecSleep(retry_interval);
- } else {
- break;
- }
- retry++;
- }
-
- ndb_mgm_destroy_handle(&m_handle);
- m_handle= 0;
- m_mgmd_port= 0;
- m_mgmd_host= 0;
- return -1;
+ConfigRetriever::do_connect(int no_retries,
+ int retry_delay_in_seconds, int verbose)
+{
+ return
+ (ndb_mgm_connect(m_handle,no_retries,retry_delay_in_seconds,verbose)==0) ?
+ 0 : -1;
}
//****************************************************************************
@@ -140,22 +111,9 @@ ConfigRetriever::getConfig() {
struct ndb_mgm_configuration * p = 0;
- if(m_handle != 0){
+ if(m_handle != 0)
p = getConfig(m_handle);
- } else {
- for (unsigned int i = 0; i<_localConfig.ids.size(); i++){
- MgmtSrvrId * m = &_localConfig.ids[i];
- switch(m->type){
- case MgmId_File:
- p = getConfig(m->name.c_str());
- break;
- case MgmId_TCP:
- break;
- }
- if(p)
- break;
- }
- }
+
if(p == 0)
return 0;
@@ -227,6 +185,16 @@ ConfigRetriever::setError(ErrorType et, const char * s){
latestErrorType = et;
}
+void
+ConfigRetriever::resetError(){
+ setError(CR_NO_ERROR,0);
+}
+
+int
+ConfigRetriever::hasError()
+{
+ return latestErrorType != CR_NO_ERROR;
+}
const char *
ConfigRetriever::getErrorString(){
@@ -341,16 +309,23 @@ ConfigRetriever::verifyConfig(const struct ndb_mgm_configuration * conf, Uint32
}
Uint32
-ConfigRetriever::allocNodeId(){
- unsigned nodeid= _ownNodeId;
-
- if(m_handle != 0){
- int res= ndb_mgm_alloc_nodeid(m_handle, m_version, &nodeid, m_node_type);
- if(res != 0) {
- setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
- return 0;
+ConfigRetriever::allocNodeId(int no_retries, int retry_delay_in_seconds)
+{
+ _ownNodeId= 0;
+ if(m_handle != 0)
+ {
+ while (1)
+ {
+ int res= ndb_mgm_alloc_nodeid(m_handle, m_version, m_node_type);
+ if(res >= 0)
+ return _ownNodeId= (Uint32)res;
+ if (no_retries == 0)
+ break;
+ no_retries--;
+ NdbSleep_SecSleep(retry_delay_in_seconds);
}
- }
-
- return _ownNodeId= nodeid;
+ setError(CR_ERROR, ndb_mgm_get_latest_error_desc(m_handle));
+ } else
+ setError(CR_ERROR, "management server handle not initialized");
+ return 0;
}
diff --git a/ndb/src/common/util/version.c b/ndb/src/common/util/version.c
index 965d0a735e1..7a537297861 100644
--- a/ndb/src/common/util/version.c
+++ b/ndb/src/common/util/version.c
@@ -70,7 +70,6 @@ struct NdbUpGradeCompatible {
#ifndef TEST_VERSION
struct NdbUpGradeCompatible ndbCompatibleTable_full[] = {
{ MAKE_VERSION(3,5,2), MAKE_VERSION(3,5,1), UG_Exact },
- { MAKE_VERSION(4,1,8), MAKE_VERSION(3,5,4), UG_Exact }, /* Aligned version with MySQL */
{ 0, 0, UG_Null }
};
diff --git a/ndb/src/kernel/blocks/dbdih/Dbdih.hpp b/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
index 14fa262f871..0a2d50cb876 100644
--- a/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
+++ b/ndb/src/kernel/blocks/dbdih/Dbdih.hpp
@@ -147,7 +147,6 @@ public:
Uint32 nfConnect;
Uint32 table;
Uint32 userpointer;
- Uint32 nodeCount;
BlockReference userblockref;
};
typedef Ptr ConnectRecordPtr;
diff --git a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
index 76aa745c3e0..4592b121c7e 100644
--- a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
+++ b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
@@ -7080,24 +7080,22 @@ void Dbdih::execDIGETPRIMREQ(Signal* signal)
ndbrequire(tabPtr.p->tabStatus == TabRecord::TS_ACTIVE);
connectPtr.i = signal->theData[0];
- if(connectPtr.i != RNIL){
+ if(connectPtr.i != RNIL)
+ {
jam();
ptrCheckGuard(connectPtr, cconnectFileSize, connectRecord);
- ndbrequire(connectPtr.p->connectState == ConnectRecord::INUSE);
- getFragstore(tabPtr.p, fragId, fragPtr);
- connectPtr.p->nodeCount = extractNodeInfo(fragPtr.p, connectPtr.p->nodes);
signal->theData[0] = connectPtr.p->userpointer;
- signal->theData[1] = passThrough;
- signal->theData[2] = connectPtr.p->nodes[0];
- sendSignal(connectPtr.p->userblockref, GSN_DIGETPRIMCONF, signal, 3, JBB);
- return;
- }//if
- //connectPtr.i == RNIL -> question without connect record
+ }
+ else
+ {
+ jam();
+ signal->theData[0] = RNIL;
+ }
+
Uint32 nodes[MAX_REPLICAS];
getFragstore(tabPtr.p, fragId, fragPtr);
Uint32 count = extractNodeInfo(fragPtr.p, nodes);
- signal->theData[0] = RNIL;
signal->theData[1] = passThrough;
signal->theData[2] = nodes[0];
signal->theData[3] = nodes[1];
diff --git a/ndb/src/kernel/blocks/dblqh/Dblqh.hpp b/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
index 739c3c741fb..0c63cb5fe17 100644
--- a/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
+++ b/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
@@ -550,6 +550,11 @@ public:
UintR scanErrorCounter;
UintR scanLocalFragid;
UintR scanSchemaVersion;
+
+ /**
+ * This is _always_ main table, even in range scan
+ * in which case scanTcrec->fragmentptr is different
+ */
Uint32 fragPtrI;
UintR scanStoredProcId;
ScanState scanState;
@@ -2925,4 +2930,23 @@ Dblqh::ScanRecord::check_scan_batch_completed() const
(max_bytes > 0 && (m_curr_batch_size_bytes >= max_bytes));
}
+inline
+void
+Dblqh::i_get_acc_ptr(ScanRecord* scanP, Uint32* &acc_ptr, Uint32 index)
+{
+ if (index == 0) {
+ acc_ptr= (Uint32*)&scanP->scan_acc_op_ptr[0];
+ } else {
+ Uint32 attr_buf_index, attr_buf_rec;
+
+ AttrbufPtr regAttrPtr;
+ jam();
+ attr_buf_rec= (index + 31) / 32;
+ attr_buf_index= (index - 1) & 31;
+ regAttrPtr.i= scanP->scan_acc_op_ptr[attr_buf_rec];
+ ptrCheckGuard(regAttrPtr, cattrinbufFileSize, attrbuf);
+ acc_ptr= (Uint32*)®AttrPtr.p->attrbuf[attr_buf_index];
+ }
+}
+
#endif
diff --git a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
index 5622706a96c..c106a6ddfac 100644
--- a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
+++ b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
@@ -3084,6 +3084,7 @@ void Dblqh::execATTRINFO(Signal* signal)
return;
break;
default:
+ ndbout_c("%d", regTcPtr->transactionState);
ndbrequire(false);
break;
}//switch
@@ -7161,10 +7162,7 @@ void Dblqh::continueScanNextReqLab(Signal* signal)
// Update timer on tcConnectRecord
tcConnectptr.p->tcTimer = cLqhTimeOutCount;
-
init_acc_ptr_list(scanptr.p);
- scanptr.p->m_curr_batch_size_rows = 0;
- scanptr.p->m_curr_batch_size_bytes= 0;
scanptr.p->scanFlag = NextScanReq::ZSCAN_NEXT;
scanNextLoopLab(signal);
}//Dblqh::continueScanNextReqLab()
@@ -7363,22 +7361,32 @@ void Dblqh::scanLockReleasedLab(Signal* signal)
tcConnectptr.i = scanptr.p->scanTcrec;
ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
releaseActiveFrag(signal);
+
if (scanptr.p->scanReleaseCounter == scanptr.p->m_curr_batch_size_rows) {
if ((scanptr.p->scanErrorCounter > 0) ||
(scanptr.p->scanCompletedStatus == ZTRUE)) {
jam();
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes = 0;
closeScanLab(signal);
} else if (scanptr.p->check_scan_batch_completed() &&
scanptr.p->scanLockHold != ZTRUE) {
jam();
scanptr.p->scanState = ScanRecord::WAIT_SCAN_NEXTREQ;
sendScanFragConf(signal, ZFALSE);
+ } else if (scanptr.p->m_last_row && !scanptr.p->scanLockHold) {
+ jam();
+ closeScanLab(signal);
+ return;
} else {
jam();
/*
- We came here after releasing locks after receiving SCAN_NEXTREQ from TC. We only
- come here when scanHoldLock == ZTRUE
- */
+ * We came here after releasing locks after
+ * receiving SCAN_NEXTREQ from TC. We only come here
+ * when scanHoldLock == ZTRUE
+ */
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes = 0;
continueScanNextReqLab(signal);
}//if
} else if (scanptr.p->scanReleaseCounter < scanptr.p->m_curr_batch_size_rows) {
@@ -7465,25 +7473,6 @@ Dblqh::init_acc_ptr_list(ScanRecord* scanP)
scanP->scan_acc_index = 0;
}
-inline
-void
-Dblqh::i_get_acc_ptr(ScanRecord* scanP, Uint32* &acc_ptr, Uint32 index)
-{
- if (index == 0) {
- acc_ptr= (Uint32*)&scanP->scan_acc_op_ptr[0];
- } else {
- Uint32 attr_buf_index, attr_buf_rec;
-
- AttrbufPtr regAttrPtr;
- jam();
- attr_buf_rec= (index + 31) / 32;
- attr_buf_index= (index - 1) & 31;
- regAttrPtr.i= scanP->scan_acc_op_ptr[attr_buf_rec];
- ptrCheckGuard(regAttrPtr, cattrinbufFileSize, attrbuf);
- acc_ptr= (Uint32*)®AttrPtr.p->attrbuf[attr_buf_index];
- }
-}
-
Uint32
Dblqh::get_acc_ptr_from_scan_record(ScanRecord* scanP,
Uint32 index,
@@ -7714,6 +7703,9 @@ void Dblqh::abort_scan(Signal* signal, Uint32 scan_ptr_i, Uint32 errcode){
jam();
scanptr.i = scan_ptr_i;
c_scanRecordPool.getPtr(scanptr);
+
+ fragptr.i = tcConnectptr.p->fragmentptr;
+ ptrCheckGuard(fragptr, cfragrecFileSize, fragrecord);
finishScanrec(signal);
releaseScanrec(signal);
tcConnectptr.p->transactionState = TcConnectionrec::IDLE;
@@ -8007,6 +7999,13 @@ void Dblqh::nextScanConfScanLab(Signal* signal)
/*************************************************************
* STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED.
************************************************************ */
+ if (!scanptr.p->scanLockHold)
+ {
+ jam();
+ closeScanLab(signal);
+ return;
+ }
+
if (scanptr.p->scanCompletedStatus == ZTRUE) {
if ((scanptr.p->scanLockHold == ZTRUE) &&
(scanptr.p->m_curr_batch_size_rows > 0)) {
@@ -8507,8 +8506,6 @@ void Dblqh::tupScanCloseConfLab(Signal* signal)
ScanFragRef::SignalLength, JBB);
} else {
jam();
- scanptr.p->m_curr_batch_size_rows = 0;
- scanptr.p->m_curr_batch_size_bytes= 0;
sendScanFragConf(signal, ZSCAN_FRAG_CLOSED);
}//if
finishScanrec(signal);
@@ -8580,7 +8577,7 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq)
tFragPtr.i = fragptr.p->tableFragptr;
ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
scanptr.p->fragPtrI = fragptr.p->tableFragptr;
-
+
/**
* !idx uses 1 - (MAX_PARALLEL_SCANS_PER_FRAG - 1) = 1-11
* idx uses from MAX_PARALLEL_SCANS_PER_FRAG - MAX = 12-42)
@@ -8589,10 +8586,10 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq)
Uint32 stop = (idx ? MAX_PARALLEL_INDEX_SCANS_PER_FRAG : MAX_PARALLEL_SCANS_PER_FRAG - 1);
stop += start;
Uint32 free = tFragPtr.p->m_scanNumberMask.find(start);
-
+
if(free == Fragrecord::ScanNumberMask::NotFound || free >= stop){
jam();
-
+
if(scanPrio == 0){
jam();
return ScanFragRef::ZTOO_MANY_ACTIVE_SCAN_ERROR;
@@ -8603,16 +8600,15 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq)
*/
scanptr.p->scanState = ScanRecord::IN_QUEUE;
LocalDLFifoList queue(c_scanRecordPool,
- tFragPtr.p->m_queuedScans);
+ fragptr.p->m_queuedScans);
queue.add(scanptr);
return ZOK;
}
-
scanptr.p->scanNumber = free;
tFragPtr.p->m_scanNumberMask.clear(free);// Update mask
-
- LocalDLList active(c_scanRecordPool, tFragPtr.p->m_activeScans);
+
+ LocalDLList active(c_scanRecordPool, fragptr.p->m_activeScans);
active.add(scanptr);
if(scanptr.p->scanKeyinfoFlag){
jam();
@@ -8672,12 +8668,8 @@ void Dblqh::finishScanrec(Signal* signal)
{
release_acc_ptr_list(scanptr.p);
- FragrecordPtr tFragPtr;
- tFragPtr.i = scanptr.p->fragPtrI;
- ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
-
LocalDLFifoList queue(c_scanRecordPool,
- tFragPtr.p->m_queuedScans);
+ fragptr.p->m_queuedScans);
if(scanptr.p->scanState == ScanRecord::IN_QUEUE){
jam();
@@ -8695,9 +8687,13 @@ void Dblqh::finishScanrec(Signal* signal)
ndbrequire(tmp.p == scanptr.p);
}
- LocalDLList scans(c_scanRecordPool, tFragPtr.p->m_activeScans);
+ LocalDLList scans(c_scanRecordPool, fragptr.p->m_activeScans);
scans.release(scanptr);
+ FragrecordPtr tFragPtr;
+ tFragPtr.i = scanptr.p->fragPtrI;
+ ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord);
+
const Uint32 scanNumber = scanptr.p->scanNumber;
ndbrequire(!tFragPtr.p->m_scanNumberMask.get(scanNumber));
ScanRecordPtr restart;
@@ -8724,7 +8720,7 @@ void Dblqh::finishScanrec(Signal* signal)
ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec);
restart.p->scanNumber = scanNumber;
restart.p->scanState = ScanRecord::WAIT_ACC_SCAN;
-
+
queue.remove(restart);
scans.add(restart);
if(restart.p->scanKeyinfoFlag){
@@ -8912,6 +8908,13 @@ void Dblqh::sendScanFragConf(Signal* signal, Uint32 scanCompleted)
conf->total_len= total_len;
sendSignal(tcConnectptr.p->clientBlockref, GSN_SCAN_FRAGCONF,
signal, ScanFragConf::SignalLength, JBB);
+
+ if(!scanptr.p->scanLockHold)
+ {
+ jam();
+ scanptr.p->m_curr_batch_size_rows = 0;
+ scanptr.p->m_curr_batch_size_bytes= 0;
+ }
}//Dblqh::sendScanFragConf()
/* ######################################################################### */
diff --git a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
index a209df24c44..fb90ccc8c90 100644
--- a/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
+++ b/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
@@ -1054,9 +1054,8 @@ public:
// Id of the ScanRecord this fragment scan belongs to
Uint32 scanRec;
- // The maximum number of operations that can be scanned before
- // returning to TC
- Uint16 scanFragConcurrency;
+ // The value of fragmentCompleted in the last received SCAN_FRAGCONF
+ Uint8 m_scan_frag_conf_status;
inline void startFragTimer(Uint32 timeVal){
scanFragTimer = timeVal;
@@ -1193,8 +1192,10 @@ public:
// Number of operation records per scanned fragment
// Number of operations in first batch
// Max number of bytes per batch
- Uint16 noOprecPerFrag;
- Uint16 first_batch_size;
+ union {
+ Uint16 first_batch_size_rows;
+ Uint16 batch_size_rows;
+ };
Uint32 batch_byte_size;
Uint32 scanRequestInfo; // ScanFrag format
diff --git a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
index d8b3ee10532..07dbb370ec6 100644
--- a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
+++ b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
@@ -8646,9 +8646,9 @@ void Dbtc::initScanrec(ScanRecordPtr scanptr,
scanptr.p->scanTableref = tabptr.i;
scanptr.p->scanSchemaVersion = scanTabReq->tableSchemaVersion;
scanptr.p->scanParallel = scanParallel;
- scanptr.p->noOprecPerFrag = noOprecPerFrag;
- scanptr.p->first_batch_size= scanTabReq->first_batch_size;
- scanptr.p->batch_byte_size= scanTabReq->batch_byte_size;
+ scanptr.p->first_batch_size_rows = scanTabReq->first_batch_size;
+ scanptr.p->batch_byte_size = scanTabReq->batch_byte_size;
+ scanptr.p->batch_size_rows = noOprecPerFrag;
Uint32 tmp = 0;
const UintR ri = scanTabReq->requestInfo;
@@ -8672,7 +8672,6 @@ void Dbtc::initScanrec(ScanRecordPtr scanptr,
ndbrequire(list.seize(ptr));
ptr.p->scanRec = scanptr.i;
ptr.p->scanFragId = 0;
- ptr.p->scanFragConcurrency = noOprecPerFrag;
ptr.p->m_apiPtr = cdata[i];
}//for
@@ -8945,6 +8944,25 @@ void Dbtc::execDIGETPRIMCONF(Signal* signal)
scanptr.i = scanFragptr.p->scanRec;
ptrCheckGuard(scanptr, cscanrecFileSize, scanRecord);
+ /**
+ * This must be false as select count(*) otherwise
+ * can "pass" committing on backup fragments and
+ * get incorrect row count
+ */
+ if(false && ScanFragReq::getReadCommittedFlag(scanptr.p->scanRequestInfo))
+ {
+ jam();
+ Uint32 max = 3+signal->theData[6];
+ Uint32 nodeid = getOwnNodeId();
+ for(Uint32 i = 3; itheData[i] == nodeid)
+ {
+ jam();
+ tnodeid = nodeid;
+ break;
+ }
+ }
+
{
/**
* Check table
@@ -9141,6 +9159,7 @@ void Dbtc::execSCAN_FRAGCONF(Signal* signal)
const ScanFragConf * const conf = (ScanFragConf*)&signal->theData[0];
const Uint32 noCompletedOps = conf->completedOps;
+ const Uint32 status = conf->fragmentCompleted;
scanFragptr.i = conf->senderData;
c_scan_frag_pool.getPtr(scanFragptr);
@@ -9163,11 +9182,9 @@ void Dbtc::execSCAN_FRAGCONF(Signal* signal)
ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::LQH_ACTIVE);
- const Uint32 status = conf->fragmentCompleted;
-
if(scanptr.p->scanState == ScanRecord::CLOSING_SCAN){
jam();
- if(status == ZFALSE){
+ if(status == 0){
/**
* We have started closing = we sent a close -> ignore this
*/
@@ -9184,11 +9201,11 @@ void Dbtc::execSCAN_FRAGCONF(Signal* signal)
return;
}
- if(status == ZCLOSED && scanptr.p->scanNextFragId < scanptr.p->scanNoFrag){
+ if(noCompletedOps == 0 && status != 0 &&
+ scanptr.p->scanNextFragId < scanptr.p->scanNoFrag){
/**
* Start on next fragment
*/
- ndbrequire(noCompletedOps == 0);
scanFragptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
scanFragptr.p->startFragTimer(ctcTimer);
@@ -9218,6 +9235,7 @@ void Dbtc::execSCAN_FRAGCONF(Signal* signal)
scanptr.p->m_queued_count++;
}
+ scanFragptr.p->m_scan_frag_conf_status = status;
scanFragptr.p->m_ops = noCompletedOps;
scanFragptr.p->m_totalLen = total_len;
scanFragptr.p->scanFragState = ScanFragRec::QUEUED_FOR_DELIVERY;
@@ -9311,7 +9329,6 @@ void Dbtc::execSCAN_NEXTREQ(Signal* signal)
/*********************************************************************
* APPLICATION IS CLOSING THE SCAN.
**********************************************************************/
- ndbrequire(len == 0);
close_scan_req(signal, scanptr, true);
return;
}//if
@@ -9330,11 +9347,12 @@ void Dbtc::execSCAN_NEXTREQ(Signal* signal)
// Copy op ptrs so I dont overwrite them when sending...
memcpy(signal->getDataPtrSend()+25, signal->getDataPtr()+4, 4 * len);
- ScanFragNextReq * nextReq = (ScanFragNextReq*)&signal->theData[0];
- nextReq->closeFlag = ZFALSE;
- nextReq->transId1 = apiConnectptr.p->transid[0];
- nextReq->transId2 = apiConnectptr.p->transid[1];
- nextReq->batch_size_bytes= scanP->batch_byte_size;
+ ScanFragNextReq tmp;
+ tmp.closeFlag = ZFALSE;
+ tmp.transId1 = apiConnectptr.p->transid[0];
+ tmp.transId2 = apiConnectptr.p->transid[1];
+ tmp.batch_size_rows = scanP->batch_size_rows;
+ tmp.batch_size_bytes = scanP->batch_byte_size;
ScanFragList running(c_scan_frag_pool, scanP->m_running_scan_frags);
ScanFragList delivered(c_scan_frag_pool, scanP->m_delivered_scan_frags);
@@ -9344,15 +9362,37 @@ void Dbtc::execSCAN_NEXTREQ(Signal* signal)
c_scan_frag_pool.getPtr(scanFragptr);
ndbrequire(scanFragptr.p->scanFragState == ScanFragRec::DELIVERED);
- scanFragptr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
scanFragptr.p->startFragTimer(ctcTimer);
-
scanFragptr.p->m_ops = 0;
- nextReq->senderData = scanFragptr.i;
- nextReq->batch_size_rows= scanFragptr.p->scanFragConcurrency;
- sendSignal(scanFragptr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
- ScanFragNextReq::SignalLength, JBB);
+ if(scanFragptr.p->m_scan_frag_conf_status)
+ {
+ /**
+ * last scan was complete
+ */
+ jam();
+ ndbrequire(scanptr.p->scanNextFragId < scanptr.p->scanNoFrag);
+ scanFragptr.p->scanFragState = ScanFragRec::WAIT_GET_PRIMCONF;
+
+ tcConnectptr.i = scanptr.p->scanTcrec;
+ ptrCheckGuard(tcConnectptr, ctcConnectFilesize, tcConnectRecord);
+ scanFragptr.p->scanFragId = scanptr.p->scanNextFragId++;
+ signal->theData[0] = tcConnectptr.p->dihConnectptr;
+ signal->theData[1] = scanFragptr.i;
+ signal->theData[2] = scanptr.p->scanTableref;
+ signal->theData[3] = scanFragptr.p->scanFragId;
+ sendSignal(cdihblockref, GSN_DIGETPRIMREQ, signal, 4, JBB);
+ }
+ else
+ {
+ jam();
+ scanFragptr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
+ ScanFragNextReq * req = (ScanFragNextReq*)signal->getDataPtrSend();
+ * req = tmp;
+ req->senderData = scanFragptr.i;
+ sendSignal(scanFragptr.p->lqhBlockref, GSN_SCAN_NEXTREQ, signal,
+ ScanFragNextReq::SignalLength, JBB);
+ }
delivered.remove(scanFragptr);
running.add(scanFragptr);
}//for
@@ -9416,7 +9456,7 @@ Dbtc::close_scan_req(Signal* signal, ScanRecordPtr scanPtr, bool req_received){
ndbrequire(curr.p->scanFragState == ScanFragRec::DELIVERED);
delivered.remove(curr);
- if(curr.p->m_ops > 0){
+ if(curr.p->m_ops > 0 && curr.p->m_scan_frag_conf_status == 0){
jam();
running.add(curr);
curr.p->scanFragState = ScanFragRec::LQH_ACTIVE;
@@ -9551,7 +9591,7 @@ void Dbtc::sendScanFragReq(Signal* signal,
req->transId1 = apiConnectptr.p->transid[0];
req->transId2 = apiConnectptr.p->transid[1];
req->clientOpPtr = scanFragP->m_apiPtr;
- req->batch_size_rows= scanFragP->scanFragConcurrency;
+ req->batch_size_rows= scanP->batch_size_rows;
req->batch_size_bytes= scanP->batch_byte_size;
sendSignal(scanFragP->lqhBlockref, GSN_SCAN_FRAGREQ, signal,
ScanFragReq::SignalLength, JBB);
@@ -9573,6 +9613,8 @@ void Dbtc::sendScanTabConf(Signal* signal, ScanRecordPtr scanPtr) {
jam();
ops += 21;
}
+
+ Uint32 left = scanPtr.p->scanNoFrag - scanPtr.p->scanNextFragId;
ScanTabConf * conf = (ScanTabConf*)&signal->theData[0];
conf->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
@@ -9588,24 +9630,25 @@ void Dbtc::sendScanTabConf(Signal* signal, ScanRecordPtr scanPtr) {
ScanFragRecPtr curr = ptr; // Remove while iterating...
queued.next(ptr);
+ bool done = curr.p->m_scan_frag_conf_status && --left;
+
* ops++ = curr.p->m_apiPtr;
- * ops++ = curr.i;
+ * ops++ = done ? RNIL : curr.i;
* ops++ = (curr.p->m_totalLen << 10) + curr.p->m_ops;
queued.remove(curr);
- if(curr.p->m_ops > 0){
+ if(!done){
delivered.add(curr);
curr.p->scanFragState = ScanFragRec::DELIVERED;
curr.p->stopFragTimer();
} else {
- (* --ops) = ScanTabConf::EndOfData; ops++;
c_scan_frag_pool.release(curr);
curr.p->scanFragState = ScanFragRec::COMPLETED;
curr.p->stopFragTimer();
}
}
}
-
+
if(scanPtr.p->m_delivered_scan_frags.isEmpty() &&
scanPtr.p->m_running_scan_frags.isEmpty()){
conf->requestInfo = op_count | ScanTabConf::EndOfData;
@@ -10424,9 +10467,8 @@ Dbtc::execDUMP_STATE_ORD(Signal* signal)
sfp.i,
sfp.p->scanFragState,
sfp.p->scanFragId);
- infoEvent(" nodeid=%d, concurr=%d, timer=%d",
+ infoEvent(" nodeid=%d, timer=%d",
refToNode(sfp.p->lqhBlockref),
- sfp.p->scanFragConcurrency,
sfp.p->scanFragTimer);
}
@@ -10504,7 +10546,7 @@ Dbtc::execDUMP_STATE_ORD(Signal* signal)
sp.p->scanAiLength,
sp.p->scanParallel,
sp.p->scanReceivedOperations,
- sp.p->noOprecPerFrag);
+ sp.p->batch_size_rows);
infoEvent(" schv=%d, tab=%d, sproc=%d",
sp.p->scanSchemaVersion,
sp.p->scanTableref,
diff --git a/ndb/src/kernel/blocks/suma/Suma.cpp b/ndb/src/kernel/blocks/suma/Suma.cpp
index d11d5f7176a..f6d9a0ac35a 100644
--- a/ndb/src/kernel/blocks/suma/Suma.cpp
+++ b/ndb/src/kernel/blocks/suma/Suma.cpp
@@ -1888,7 +1888,7 @@ SumaParticipant::SyncRecord::nextScan(Signal* signal){
req->requestInfo = 0;
req->savePointId = 0;
ScanFragReq::setLockMode(req->requestInfo, 0);
- ScanFragReq::setHoldLockFlag(req->requestInfo, 0);
+ ScanFragReq::setHoldLockFlag(req->requestInfo, 1);
ScanFragReq::setKeyinfoFlag(req->requestInfo, 0);
ScanFragReq::setAttrLen(req->requestInfo, attrLen);
req->fragmentNoKeyLen = fd.m_fragDesc.m_fragmentNo;
diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp
index 926647838c9..f34e16318cd 100644
--- a/ndb/src/kernel/main.cpp
+++ b/ndb/src/kernel/main.cpp
@@ -19,7 +19,6 @@
#include
#include "Configuration.hpp"
-#include
#include
#include "vm/SimBlockList.hpp"
@@ -69,16 +68,9 @@ int main(int argc, char** argv)
return NRT_Default;
}
- LocalConfig local_config;
- if (!local_config.init(theConfig->getConnectString(),0)){
- local_config.printError();
- local_config.printUsage();
- return NRT_Default;
- }
-
{ // Do configuration
signal(SIGPIPE, SIG_IGN);
- theConfig->fetch_configuration(local_config);
+ theConfig->fetch_configuration();
}
chdir(NdbConfig_get_path(0));
@@ -141,7 +133,7 @@ int main(int argc, char** argv)
exit(0);
}
g_eventLogger.info("Ndb has terminated (pid %d) restarting", child);
- theConfig->fetch_configuration(local_config);
+ theConfig->fetch_configuration();
}
g_eventLogger.info("Angel pid: %d ndb pid: %d", getppid(), getpid());
diff --git a/ndb/src/kernel/vm/Configuration.cpp b/ndb/src/kernel/vm/Configuration.cpp
index aac035fe1b7..931b4da5a17 100644
--- a/ndb/src/kernel/vm/Configuration.cpp
+++ b/ndb/src/kernel/vm/Configuration.cpp
@@ -17,7 +17,6 @@
#include
#include
-#include
#include "Configuration.hpp"
#include
#include "GlobalData.hpp"
@@ -35,6 +34,7 @@
#include
#include
+#include
#include "pc.hpp"
#include
#include
@@ -108,7 +108,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
bool
Configuration::init(int argc, char** argv)
{
- const char *load_default_groups[]= { "ndbd",0 };
+ const char *load_default_groups[]= { "mysql_cluster","ndbd",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
@@ -189,7 +189,7 @@ Configuration::closeConfiguration(){
}
void
-Configuration::fetch_configuration(LocalConfig &local_config){
+Configuration::fetch_configuration(){
/**
* Fetch configuration from management server
*/
@@ -199,8 +199,17 @@ Configuration::fetch_configuration(LocalConfig &local_config){
m_mgmd_port= 0;
m_mgmd_host= 0;
- m_config_retriever= new ConfigRetriever(local_config, NDB_VERSION, NODE_TYPE_DB);
- if(m_config_retriever->do_connect() == -1){
+ m_config_retriever= new ConfigRetriever(getConnectString(),
+ NDB_VERSION, NODE_TYPE_DB);
+
+ if (m_config_retriever->hasError())
+ {
+ ERROR_SET(fatal, ERR_INVALID_CONFIG,
+ "Could not connect initialize handle to management server",
+ m_config_retriever->getErrorString());
+ }
+
+ if(m_config_retriever->do_connect(12,5,1) == -1){
const char * s = m_config_retriever->getErrorString();
if(s == 0)
s = "No error given!";
@@ -215,13 +224,7 @@ Configuration::fetch_configuration(LocalConfig &local_config){
ConfigRetriever &cr= *m_config_retriever;
- if((globalData.ownId = cr.allocNodeId()) == 0){
- for(Uint32 i = 0; i<3; i++){
- NdbSleep_SecSleep(3);
- if((globalData.ownId = cr.allocNodeId()) != 0)
- break;
- }
- }
+ globalData.ownId = cr.allocNodeId(2 /*retry*/,3 /*delay*/);
if(globalData.ownId == 0){
ERROR_SET(fatal, ERR_INVALID_CONFIG,
@@ -452,6 +455,7 @@ Configuration::calcSizeAlt(ConfigValues * ownConfig){
unsigned int noOfTables = 0;
unsigned int noOfUniqueHashIndexes = 0;
unsigned int noOfOrderedIndexes = 0;
+ unsigned int noOfTriggers = 0;
unsigned int noOfReplicas = 0;
unsigned int noOfDBNodes = 0;
unsigned int noOfAPINodes = 0;
@@ -476,6 +480,7 @@ Configuration::calcSizeAlt(ConfigValues * ownConfig){
{ CFG_DB_NO_TABLES, &noOfTables, false },
{ CFG_DB_NO_ORDERED_INDEXES, &noOfOrderedIndexes, false },
{ CFG_DB_NO_UNIQUE_HASH_INDEXES, &noOfUniqueHashIndexes, false },
+ { CFG_DB_NO_TRIGGERS, &noOfTriggers, true },
{ CFG_DB_NO_REPLICAS, &noOfReplicas, false },
{ CFG_DB_NO_ATTRIBUTES, &noOfAttributes, false },
{ CFG_DB_NO_OPS, &noOfOperations, false },
@@ -584,6 +589,18 @@ Configuration::calcSizeAlt(ConfigValues * ownConfig){
ConfigValues::Iterator it2(*ownConfig, db.m_config);
it2.set(CFG_DB_NO_TABLES, noOfTables);
it2.set(CFG_DB_NO_ATTRIBUTES, noOfAttributes);
+ {
+ Uint32 neededNoOfTriggers = /* types: Insert/Update/Delete/Custom */
+ 3 * noOfUniqueHashIndexes + /* for unique hash indexes, I/U/D */
+ 3 * NDB_MAX_ACTIVE_EVENTS + /* for events in suma, I/U/D */
+ 3 * noOfTables + /* for backup, I/U/D */
+ noOfOrderedIndexes; /* for ordered indexes, C */
+ if (noOfTriggers < neededNoOfTriggers)
+ {
+ noOfTriggers= neededNoOfTriggers;
+ it2.set(CFG_DB_NO_TRIGGERS, noOfTriggers);
+ }
+ }
/**
* Do size calculations
diff --git a/ndb/src/kernel/vm/Configuration.hpp b/ndb/src/kernel/vm/Configuration.hpp
index e4cd64f5ca8..acf0e163a84 100644
--- a/ndb/src/kernel/vm/Configuration.hpp
+++ b/ndb/src/kernel/vm/Configuration.hpp
@@ -21,7 +21,6 @@
#include
class ConfigRetriever;
-class LocalConfig;
class Configuration {
public:
@@ -33,7 +32,7 @@ public:
*/
bool init(int argc, char** argv);
- void fetch_configuration(LocalConfig &local_config);
+ void fetch_configuration();
void setupConfiguration();
void closeConfiguration();
diff --git a/ndb/src/mgmapi/LocalConfig.cpp b/ndb/src/mgmapi/LocalConfig.cpp
index d0ff97cdedf..8f1e2ee8100 100644
--- a/ndb/src/mgmapi/LocalConfig.cpp
+++ b/ndb/src/mgmapi/LocalConfig.cpp
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include
+#include "LocalConfig.hpp"
#include
#include
#include
@@ -294,4 +294,19 @@ LocalConfig::readConnectString(const char * connectString,
return return_value;
}
+char *
+LocalConfig::makeConnectString(char *buf, int sz)
+{
+ int p= BaseString::snprintf(buf,sz,"nodeid=%d", _ownNodeId);
+ for (int i = 0; (i < ids.size()) && (sz-p > 0); i++)
+ {
+ if (ids[i].type != MgmId_TCP)
+ continue;
+ p+=BaseString::snprintf(buf+p,sz-p,",%s:%d",
+ ids[i].name.c_str(), ids[i].port);
+ }
+ buf[sz-1]=0;
+ return buf;
+}
+
template class Vector;
diff --git a/ndb/include/mgmapi/LocalConfig.hpp b/ndb/src/mgmapi/LocalConfig.hpp
similarity index 97%
rename from ndb/include/mgmapi/LocalConfig.hpp
rename to ndb/src/mgmapi/LocalConfig.hpp
index 9ceeffdba36..c415ec1be91 100644
--- a/ndb/include/mgmapi/LocalConfig.hpp
+++ b/ndb/src/mgmapi/LocalConfig.hpp
@@ -61,6 +61,7 @@ struct LocalConfig {
bool parseHostName(const char *buf);
bool parseFileName(const char *buf);
bool parseString(const char *buf, BaseString &err);
+ char * makeConnectString(char *buf, int sz);
};
#endif // LocalConfig_H
diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp
index 51f2d7cee01..ca3a2a2186d 100644
--- a/ndb/src/mgmapi/mgmapi.cpp
+++ b/ndb/src/mgmapi/mgmapi.cpp
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include "mgmapi.h"
#include "mgmapi_debug.h"
@@ -83,8 +84,8 @@ typedef Parser Parser_t;
#define NDB_MGM_MAX_ERR_DESC_SIZE 256
struct ndb_mgm_handle {
- char * hostname;
- unsigned short port;
+ char * connectstring;
+ int cfg_i;
int connected;
int last_error;
@@ -95,7 +96,7 @@ struct ndb_mgm_handle {
NDB_SOCKET_TYPE socket;
- char cfg_ptr[sizeof(LocalConfig)];
+ LocalConfig cfg;
#ifdef MGMAPI_LOG
FILE* logfile;
@@ -148,14 +149,16 @@ ndb_mgm_create_handle()
h->connected = 0;
h->last_error = 0;
h->last_error_line = 0;
- h->hostname = 0;
h->socket = NDB_INVALID_SOCKET;
h->read_timeout = 50000;
h->write_timeout = 100;
-
- new (h->cfg_ptr) LocalConfig;
+ h->cfg_i = 0;
strncpy(h->last_error_desc, "No error", NDB_MGM_MAX_ERR_DESC_SIZE);
+
+ new (&(h->cfg)) LocalConfig;
+ h->cfg.init(0, 0);
+
#ifdef MGMAPI_LOG
h->logfile = 0;
#endif
@@ -163,6 +166,23 @@ ndb_mgm_create_handle()
return h;
}
+extern "C"
+int
+ndb_mgm_set_connectstring(NdbMgmHandle handle, const char * mgmsrv)
+{
+ new (&(handle->cfg)) LocalConfig;
+ if (!handle->cfg.init(mgmsrv, 0) ||
+ handle->cfg.ids.size() == 0)
+ {
+ new (&(handle->cfg)) LocalConfig;
+ handle->cfg.init(0, 0); /* reset the LocalCongig */
+ SET_ERROR(handle, NDB_MGM_ILLEGAL_CONNECT_STRING, "");
+ return -1;
+ }
+ handle->cfg_i= 0;
+ return 0;
+}
+
/**
* Destroy a handle
*/
@@ -175,14 +195,13 @@ ndb_mgm_destroy_handle(NdbMgmHandle * handle)
if((* handle)->connected){
ndb_mgm_disconnect(* handle);
}
- my_free((* handle)->hostname,MYF(MY_ALLOW_ZERO_PTR));
#ifdef MGMAPI_LOG
if ((* handle)->logfile != 0){
fclose((* handle)->logfile);
(* handle)->logfile = 0;
}
#endif
- ((LocalConfig*)((*handle)->cfg_ptr))->~LocalConfig();
+ (*handle)->cfg.~LocalConfig();
my_free((char*)* handle,MYF(MY_ALLOW_ZERO_PTR));
* handle = 0;
}
@@ -314,7 +333,8 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow *command_reply,
*/
extern "C"
int
-ndb_mgm_connect(NdbMgmHandle handle, const char * mgmsrv)
+ndb_mgm_connect(NdbMgmHandle handle, int no_retries,
+ int retry_delay_in_seconds, int verbose)
{
SET_ERROR(handle, NDB_MGM_NO_ERROR, "Executing: ndb_mgm_connect");
CHECK_HANDLE(handle, -1);
@@ -331,36 +351,48 @@ ndb_mgm_connect(NdbMgmHandle handle, const char * mgmsrv)
/**
* Do connect
*/
- LocalConfig *cfg= (LocalConfig*)(handle->cfg_ptr);
- new (cfg) LocalConfig;
- if (!cfg->init(mgmsrv, 0) ||
- cfg->ids.size() == 0)
- {
- SET_ERROR(handle, NDB_MGM_ILLEGAL_CONNECT_STRING, "");
- return -1;
- }
-
+ LocalConfig &cfg= handle->cfg;
NDB_SOCKET_TYPE sockfd= NDB_INVALID_SOCKET;
Uint32 i;
- for (i = 0; i < cfg->ids.size(); i++)
+ while (sockfd == NDB_INVALID_SOCKET)
{
- if (cfg->ids[i].type != MgmId_TCP)
- continue;
- SocketClient s(cfg->ids[i].name.c_str(), cfg->ids[i].port);
- sockfd = s.connect();
+ // do all the mgmt servers
+ for (i = 0; i < cfg.ids.size(); i++)
+ {
+ if (cfg.ids[i].type != MgmId_TCP)
+ continue;
+ SocketClient s(cfg.ids[i].name.c_str(), cfg.ids[i].port);
+ sockfd = s.connect();
+ if (sockfd != NDB_INVALID_SOCKET)
+ break;
+ }
if (sockfd != NDB_INVALID_SOCKET)
break;
- }
- if (sockfd == NDB_INVALID_SOCKET)
- {
- setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
- "Unable to connect using connectstring %s", mgmsrv);
- return -1;
+ if (verbose > 0) {
+ char buf[1024];
+ ndbout_c("Unable to connect with connect string: %s",
+ cfg.makeConnectString(buf,sizeof(buf)));
+ verbose= -1;
+ }
+ if (no_retries == 0) {
+ char buf[1024];
+ setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
+ "Unable to connect with connect string: %s",
+ cfg.makeConnectString(buf,sizeof(buf)));
+ return -1;
+ }
+ if (verbose == -1) {
+ ndbout << "retrying every " << retry_delay_in_seconds << " seconds:";
+ verbose= -2;
+ }
+ NdbSleep_SecSleep(retry_delay_in_seconds);
+ if (verbose == -2) {
+ ndbout << " " << no_retries;
+ }
+ no_retries--;
}
- my_free(handle->hostname,MYF(MY_ALLOW_ZERO_PTR));
- handle->hostname = my_strdup(cfg->ids[i].name.c_str(),MYF(MY_WME));
- handle->port = cfg->ids[i].port;
+ handle->cfg_i = i;
handle->socket = sockfd;
handle->connected = 1;
@@ -1068,7 +1100,9 @@ ndb_mgm_listen_event(NdbMgmHandle handle, int filter[])
};
CHECK_HANDLE(handle, -1);
- SocketClient s(handle->hostname, handle->port);
+ const char *hostname= ndb_mgm_get_connected_host(handle);
+ int port= ndb_mgm_get_connected_port(handle);
+ SocketClient s(hostname, port);
const NDB_SOCKET_TYPE sockfd = s.connect();
if (sockfd < 0) {
setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
@@ -1613,16 +1647,37 @@ ndb_mgm_destroy_configuration(struct ndb_mgm_configuration *cfg)
extern "C"
int
-ndb_mgm_alloc_nodeid(NdbMgmHandle handle, unsigned int version, unsigned *pnodeid, int nodetype)
+ndb_mgm_get_configuration_nodeid(NdbMgmHandle handle)
{
+ CHECK_HANDLE(handle, 0);
+ return handle->cfg._ownNodeId;
+}
+extern "C"
+int ndb_mgm_get_connected_port(NdbMgmHandle handle)
+{
+ return handle->cfg.ids[handle->cfg_i].port;
+}
+
+extern "C"
+const char *ndb_mgm_get_connected_host(NdbMgmHandle handle)
+{
+ return handle->cfg.ids[handle->cfg_i].name.c_str();
+}
+
+extern "C"
+int
+ndb_mgm_alloc_nodeid(NdbMgmHandle handle, unsigned int version, int nodetype)
+{
CHECK_HANDLE(handle, 0);
CHECK_CONNECTED(handle, 0);
+ int nodeid= handle->cfg._ownNodeId;
+
Properties args;
args.put("version", version);
args.put("nodetype", nodetype);
- args.put("nodeid", *pnodeid);
+ args.put("nodeid", nodeid);
args.put("user", "mysqld");
args.put("password", "mysqld");
args.put("public key", "a public key");
@@ -1638,26 +1693,29 @@ ndb_mgm_alloc_nodeid(NdbMgmHandle handle, unsigned int version, unsigned *pnodei
prop= ndb_mgm_call(handle, reply, "get nodeid", &args);
CHECK_REPLY(prop, -1);
- int res= -1;
+ nodeid= -1;
do {
const char * buf;
if(!prop->get("result", &buf) || strcmp(buf, "Ok") != 0){
+ const char *hostname= ndb_mgm_get_connected_host(handle);
+ unsigned port= ndb_mgm_get_connected_port(handle);
BaseString err;
err.assfmt("Could not alloc node id at %s port %d: %s",
- handle->hostname, handle->port, buf);
+ hostname, port, buf);
setError(handle, NDB_MGM_COULD_NOT_CONNECT_TO_SOCKET, __LINE__,
err.c_str());
break;
}
- if(!prop->get("nodeid", pnodeid) != 0){
+ Uint32 _nodeid;
+ if(!prop->get("nodeid", &_nodeid) != 0){
ndbout_c("ERROR Message: \n");
break;
}
- res= 0;
+ nodeid= _nodeid;
}while(0);
delete prop;
- return res;
+ return nodeid;
}
/*****************************************************************************
diff --git a/ndb/src/mgmclient/CommandInterpreter.cpp b/ndb/src/mgmclient/CommandInterpreter.cpp
index bdeb885ed8b..54beaa49d3f 100644
--- a/ndb/src/mgmclient/CommandInterpreter.cpp
+++ b/ndb/src/mgmclient/CommandInterpreter.cpp
@@ -153,7 +153,6 @@ private:
NdbMgmHandle m_mgmsrv;
bool connected;
- const char *host;
int try_reconnect;
#ifdef HAVE_GLOBAL_REPLICATION
NdbRepHandle m_repserver;
@@ -379,15 +378,16 @@ CommandInterpreter::CommandInterpreter(const char *_host)
m_mgmsrv = ndb_mgm_create_handle();
if(m_mgmsrv == NULL) {
ndbout_c("Cannot create handle to management server.");
+ exit(-1);
+ }
+ if (ndb_mgm_set_connectstring(m_mgmsrv, _host))
+ {
printError();
+ exit(-1);
}
connected = false;
try_reconnect = 0;
- if (_host)
- host= my_strdup(_host,MYF(MY_WME));
- else
- host= 0;
#ifdef HAVE_GLOBAL_REPLICATION
rep_host = NULL;
m_repserver = NULL;
@@ -402,8 +402,6 @@ CommandInterpreter::~CommandInterpreter()
{
connected = false;
ndb_mgm_destroy_handle(&m_mgmsrv);
- my_free((char *)host,MYF(MY_ALLOW_ZERO_PTR));
- host = NULL;
}
static bool
@@ -438,18 +436,8 @@ bool
CommandInterpreter::connect()
{
if(!connected) {
- int tries = try_reconnect; // tries == 0 => infinite
- while(!connected) {
- if(ndb_mgm_connect(m_mgmsrv, host) == -1) {
- ndbout << "Cannot connect to management server (" << host << ").";
- tries--;
- if (tries == 0)
- break;
- ndbout << "Retrying in 5 seconds." << endl;
- NdbSleep_SecSleep(5);
- } else
- connected = true;
- }
+ if(!ndb_mgm_connect(m_mgmsrv, try_reconnect-1, 5, 1))
+ connected = true;
}
return connected;
}
diff --git a/ndb/src/mgmclient/main.cpp b/ndb/src/mgmclient/main.cpp
index 401a9198f30..f32cc683296 100644
--- a/ndb/src/mgmclient/main.cpp
+++ b/ndb/src/mgmclient/main.cpp
@@ -30,9 +30,10 @@ extern "C" int add_history(const char *command); /* From readline directory */
#include
#include
+#include
+#include
#include
#include
-#include
#include "ndb_mgmclient.hpp"
@@ -138,7 +139,7 @@ int main(int argc, char** argv){
NDB_INIT(argv[0]);
const char *_host = 0;
int _port = 0;
- const char *load_default_groups[]= { "ndb_mgm",0 };
+ const char *load_default_groups[]= { "mysql_cluster","ndb_mgm",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
diff --git a/ndb/src/mgmsrv/InitConfigFileParser.cpp b/ndb/src/mgmsrv/InitConfigFileParser.cpp
index fdfe7823fc2..05102255eaa 100644
--- a/ndb/src/mgmsrv/InitConfigFileParser.cpp
+++ b/ndb/src/mgmsrv/InitConfigFileParser.cpp
@@ -213,48 +213,41 @@ InitConfigFileParser::parseConfig(FILE * file) {
// Parse Name-Value Pair
//****************************************************************************
-bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line) {
-
- char tmpLine[MAX_LINE_LENGTH];
- char fname[MAX_LINE_LENGTH], rest[MAX_LINE_LENGTH];
- char* t;
- const char *separator_list[]= {":", "=", 0};
- const char *separator= 0;
-
+bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line)
+{
if (ctx.m_currentSection == NULL){
ctx.reportError("Value specified outside section");
return false;
}
- strncpy(tmpLine, line, MAX_LINE_LENGTH);
-
// *************************************
- // Check if a separator exists in line
+ // Split string at first occurrence of
+ // '=' or ':'
// *************************************
- for(int i= 0; separator_list[i] != 0; i++) {
- if(strchr(tmpLine, separator_list[i][0])) {
- separator= separator_list[i];
- break;
- }
- }
- if (separator == 0) {
+ Vector tmp_string_split;
+ if (BaseString(line).split(tmp_string_split,
+ BaseString("=:"),
+ 2) != 2)
+ {
ctx.reportError("Parse error");
return false;
}
- // *******************************************
- // Get pointer to substring before separator
- // *******************************************
- t = strtok(tmpLine, separator);
-
- // *****************************************
- // Count number of tokens before separator
- // *****************************************
- if (sscanf(t, "%120s%120s", fname, rest) != 1) {
- ctx.reportError("Multiple names before \'%c\'", separator[0]);
- return false;
+ // *************************************
+ // Remove leading and trailing chars
+ // *************************************
+ {
+ for (int i = 0; i < 2; i++)
+ tmp_string_split[i].trim("\r\n \t");
}
+
+ // *************************************
+ // First in split is fname
+ // *************************************
+
+ const char *fname= tmp_string_split[0].c_str();
+
if (!ctx.m_currentInfo->contains(fname)) {
ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname);
return false;
@@ -273,24 +266,11 @@ bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line) {
}
}
- // ******************************************
- // Get pointer to substring after separator
- // ******************************************
- t = strtok(NULL, "\0");
- if (t == NULL) {
- ctx.reportError("No value for parameter");
- return false;
- }
-
- // ******************************************
- // Remove prefix and postfix spaces and tabs
- // *******************************************
- trim(t);
-
// ***********************
// Store name-value pair
// ***********************
- return storeNameValuePair(ctx, fname, t);
+
+ return storeNameValuePair(ctx, fname, tmp_string_split[1].c_str());
}
diff --git a/ndb/src/mgmsrv/MgmtSrvr.cpp b/ndb/src/mgmsrv/MgmtSrvr.cpp
index a49b29af275..81b5eb9dfb3 100644
--- a/ndb/src/mgmsrv/MgmtSrvr.cpp
+++ b/ndb/src/mgmsrv/MgmtSrvr.cpp
@@ -399,16 +399,20 @@ MgmtSrvr::getPort() const {
}
/* Constructor */
-MgmtSrvr::MgmtSrvr(NodeId nodeId,
- SocketServer *socket_server,
- const BaseString &configFilename,
- LocalConfig &local_config,
- Config * config):
+int MgmtSrvr::init()
+{
+ if ( _ownNodeId > 0)
+ return 0;
+ return -1;
+}
+
+MgmtSrvr::MgmtSrvr(SocketServer *socket_server,
+ const char *config_filename,
+ const char *connect_string) :
_blockNumber(1), // Hard coded block number since it makes it easy to send
// signals to other management servers.
m_socket_server(socket_server),
_ownReference(0),
- m_local_config(local_config),
theSignalIdleList(NULL),
theWaitState(WAIT_SUBSCRIBE_CONF),
m_statisticsListner(this)
@@ -416,6 +420,8 @@ MgmtSrvr::MgmtSrvr(NodeId nodeId,
DBUG_ENTER("MgmtSrvr::MgmtSrvr");
+ _ownNodeId= 0;
+
_config = NULL;
_isStopThread = false;
@@ -426,12 +432,43 @@ MgmtSrvr::MgmtSrvr(NodeId nodeId,
theFacade = 0;
m_newConfig = NULL;
- m_configFilename = configFilename;
+ m_configFilename.assign(config_filename);
m_nextConfigGenerationNumber = 0;
- _config = (config == 0 ? readConfig() : config);
-
+ m_config_retriever= new ConfigRetriever(connect_string,
+ NDB_VERSION, NDB_MGM_NODE_TYPE_MGM);
+
+ // first try to allocate nodeid from another management server
+ if(m_config_retriever->do_connect(0,0,0) == 0)
+ {
+ int tmp_nodeid= 0;
+ tmp_nodeid= m_config_retriever->allocNodeId(0 /*retry*/,0 /*delay*/);
+ if (tmp_nodeid == 0)
+ {
+ ndbout_c(m_config_retriever->getErrorString());
+ exit(-1);
+ }
+ // read config from other managent server
+ _config= fetchConfig();
+ if (_config == 0)
+ {
+ ndbout << m_config_retriever->getErrorString() << endl;
+ exit(-1);
+ }
+ _ownNodeId= tmp_nodeid;
+ }
+
+ if (_ownNodeId == 0)
+ {
+ // read config locally
+ _config= readConfig();
+ if (_config == 0) {
+ ndbout << "Unable to read config file" << endl;
+ exit(-1);
+ }
+ }
+
theMgmtWaitForResponseCondPtr = NdbCondition_Create();
m_configMutex = NdbMutex_Create();
@@ -443,9 +480,11 @@ MgmtSrvr::MgmtSrvr(NodeId nodeId,
nodeTypes[i] = (enum ndb_mgm_node_type)-1;
m_connect_address[i].s_addr= 0;
}
+
{
- ndb_mgm_configuration_iterator * iter = ndb_mgm_create_configuration_iterator
- (config->m_configValues, CFG_SECTION_NODE);
+ ndb_mgm_configuration_iterator
+ *iter = ndb_mgm_create_configuration_iterator(_config->m_configValues,
+ CFG_SECTION_NODE);
for(ndb_mgm_first(iter); ndb_mgm_valid(iter); ndb_mgm_next(iter)){
unsigned type, id;
if(ndb_mgm_get_int_parameter(iter, CFG_TYPE_OF_SECTION, &type) != 0)
@@ -478,8 +517,6 @@ MgmtSrvr::MgmtSrvr(NodeId nodeId,
}
_props = NULL;
- _ownNodeId= 0;
- NodeId tmp= nodeId;
BaseString error_string;
if ((m_node_id_mutex = NdbMutex_Create()) == 0)
@@ -488,43 +525,25 @@ MgmtSrvr::MgmtSrvr(NodeId nodeId,
exit(-1);
}
-#if 0
- char my_hostname[256];
- struct sockaddr_in tmp_addr;
- SOCKET_SIZE_TYPE addrlen= sizeof(tmp_addr);
- if (!g_no_nodeid_checks) {
- if (gethostname(my_hostname, sizeof(my_hostname))) {
- ndbout << "error: gethostname() - " << strerror(errno) << endl;
- exit(-1);
- }
- if (Ndb_getInAddr(&(((sockaddr_in*)&tmp_addr)->sin_addr),my_hostname)) {
- ndbout << "error: Ndb_getInAddr(" << my_hostname << ") - "
- << strerror(errno) << endl;
+ if (_ownNodeId == 0) // we did not get node id from other server
+ {
+ NodeId tmp= m_config_retriever->get_configuration_nodeid();
+
+ if (!alloc_node_id(&tmp, NDB_MGM_NODE_TYPE_MGM,
+ 0, 0, error_string)){
+ ndbout << "Unable to obtain requested nodeid: "
+ << error_string.c_str() << endl;
exit(-1);
}
+ _ownNodeId = tmp;
}
- if (!alloc_node_id(&tmp, NDB_MGM_NODE_TYPE_MGM,
- (struct sockaddr *)&tmp_addr,
- &addrlen, error_string)){
- ndbout << "Unable to obtain requested nodeid: "
- << error_string.c_str() << endl;
- exit(-1);
- }
-#else
- if (!alloc_node_id(&tmp, NDB_MGM_NODE_TYPE_MGM,
- 0, 0, error_string)){
- ndbout << "Unable to obtain requested nodeid: "
- << error_string.c_str() << endl;
- exit(-1);
- }
-#endif
- _ownNodeId = tmp;
{
DBUG_PRINT("info", ("verifyConfig"));
- ConfigRetriever cr(m_local_config, NDB_VERSION, NDB_MGM_NODE_TYPE_MGM);
- if (!cr.verifyConfig(config->m_configValues, _ownNodeId)) {
- ndbout << cr.getErrorString() << endl;
+ if (!m_config_retriever->verifyConfig(_config->m_configValues,
+ _ownNodeId))
+ {
+ ndbout << m_config_retriever->getErrorString() << endl;
exit(-1);
}
}
@@ -657,6 +676,8 @@ MgmtSrvr::~MgmtSrvr()
NdbThread_WaitFor(m_signalRecvThread, &res);
NdbThread_Destroy(&m_signalRecvThread);
}
+ if (m_config_retriever)
+ delete m_config_retriever;
}
//****************************************************************************
diff --git a/ndb/src/mgmsrv/MgmtSrvr.hpp b/ndb/src/mgmsrv/MgmtSrvr.hpp
index b3257491123..2ab11250d81 100644
--- a/ndb/src/mgmsrv/MgmtSrvr.hpp
+++ b/ndb/src/mgmsrv/MgmtSrvr.hpp
@@ -175,11 +175,10 @@ public:
/* Constructor */
- MgmtSrvr(NodeId nodeId, /* Local nodeid */
- SocketServer *socket_server,
- const BaseString &config_filename, /* Where to save config */
- LocalConfig &local_config, /* Ndb.cfg filename */
- Config * config);
+ MgmtSrvr(SocketServer *socket_server,
+ const char *config_filename, /* Where to save config */
+ const char *connect_string);
+ int init();
NodeId getOwnNodeId() const {return _ownNodeId;};
/**
@@ -538,7 +537,6 @@ private:
NdbMutex *m_configMutex;
const Config * _config;
Config * m_newConfig;
- LocalConfig &m_local_config;
BaseString m_configFilename;
Uint32 m_nextConfigGenerationNumber;
@@ -755,6 +753,9 @@ private:
Config *_props;
int send(class NdbApiSignal* signal, Uint32 node, Uint32 node_type);
+
+ ConfigRetriever *m_config_retriever;
+
public:
/**
* This method does not exist
diff --git a/ndb/src/mgmsrv/MgmtSrvrConfig.cpp b/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
index 1d51061e909..6c4b4e9ae3c 100644
--- a/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
+++ b/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
@@ -272,30 +272,20 @@ MgmtSrvr::saveConfig(const Config *conf) {
Config *
MgmtSrvr::readConfig() {
- Config *conf = NULL;
- if(m_configFilename.length() != 0) {
- /* Use config file */
- InitConfigFileParser parser;
- conf = parser.parseConfig(m_configFilename.c_str());
-
- if(conf == NULL) {
- /* Try to get configuration from other MGM server */
- return fetchConfig();
- }
- }
+ Config *conf;
+ InitConfigFileParser parser;
+ conf = parser.parseConfig(m_configFilename.c_str());
return conf;
}
Config *
MgmtSrvr::fetchConfig() {
- ConfigRetriever cr(m_local_config, NDB_VERSION, NODE_TYPE_MGM);
- struct ndb_mgm_configuration * tmp = cr.getConfig();
+ struct ndb_mgm_configuration * tmp = m_config_retriever->getConfig();
if(tmp != 0){
Config * conf = new Config();
conf->m_configValues = tmp;
return conf;
}
-
return 0;
}
diff --git a/ndb/src/mgmsrv/main.cpp b/ndb/src/mgmsrv/main.cpp
index b588a2d0933..84ff98626e5 100644
--- a/ndb/src/mgmsrv/main.cpp
+++ b/ndb/src/mgmsrv/main.cpp
@@ -62,7 +62,6 @@ struct MgmGlobals {
int non_interactive;
int interactive;
const char * config_filename;
- const char * local_config_filename;
/** Stuff found in environment or in local config */
NodeId localNodeId;
@@ -70,9 +69,6 @@ struct MgmGlobals {
char * interface_name;
int port;
- /** The configuration of the cluster */
- Config * cluster_config;
-
/** The Mgmt Server */
MgmtSrvr * mgmObject;
@@ -86,9 +82,6 @@ static MgmGlobals glob;
/******************************************************************************
* Function prototypes
******************************************************************************/
-static bool readLocalConfig();
-static bool readGlobalConfig();
-
/**
* Global variables
*/
@@ -100,16 +93,28 @@ static char *opt_connect_str= 0;
static struct my_option my_long_options[] =
{
- NDB_STD_OPTS("ndb_mgm"),
+#ifndef DBUG_OFF
+ { "debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 },
+#endif
+ { "usage", '?', "Display this help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "help", '?', "Display this help and exit.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "version", 'V', "Output version information and exit.", 0, 0, 0,
+ GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+ { "connect-string", 1023,
+ "Set connect string for connecting to ndb_mgmd. "
+ "=\"host=[;nodeid=]\". "
+ "Overides specifying entries in NDB_CONNECTSTRING and config file",
+ (gptr*) &opt_connect_str, (gptr*) &opt_connect_str, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "config-file", 'f', "Specify cluster configuration file",
(gptr*) &glob.config_filename, (gptr*) &glob.config_filename, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "daemon", 'd', "Run ndb_mgmd in daemon mode (default)",
(gptr*) &glob.daemon, (gptr*) &glob.daemon, 0,
GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
- { "l", 'l', "Specify configuration file connect string (default Ndb.cfg if available)",
- (gptr*) &glob.local_config_filename, (gptr*) &glob.local_config_filename, 0,
- GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "interactive", 256, "Run interactive. Not supported but provided for testing purposes",
(gptr*) &glob.interactive, (gptr*) &glob.interactive, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
@@ -173,7 +178,7 @@ int main(int argc, char** argv)
global_mgmt_server_check = 1;
glob.config_filename= "config.ini";
- const char *load_default_groups[]= { "ndb_mgmd",0 };
+ const char *load_default_groups[]= { "mysql_cluster","ndb_mgmd",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
@@ -189,29 +194,16 @@ int main(int argc, char** argv)
MgmApiService * mapi = new MgmApiService();
- /****************************
- * Read configuration files *
- ****************************/
- LocalConfig local_config;
- if(!local_config.init(opt_connect_str,glob.local_config_filename)){
- local_config.printError();
- goto error_end;
- }
- glob.localNodeId = local_config._ownNodeId;
+ glob.mgmObject = new MgmtSrvr(glob.socketServer,
+ glob.config_filename,
+ opt_connect_str);
- if (!readGlobalConfig())
+ if (glob.mgmObject->init())
goto error_end;
- glob.mgmObject = new MgmtSrvr(glob.localNodeId, glob.socketServer,
- BaseString(glob.config_filename),
- local_config,
- glob.cluster_config);
-
chdir(NdbConfig_get_path(0));
- glob.cluster_config = 0;
glob.localNodeId= glob.mgmObject->getOwnNodeId();
-
if (glob.localNodeId == 0) {
goto error_end;
}
@@ -322,9 +314,7 @@ MgmGlobals::MgmGlobals(){
// Default values
port = 0;
config_filename = NULL;
- local_config_filename = NULL;
interface_name = 0;
- cluster_config = 0;
daemon = 1;
non_interactive = 0;
interactive = 0;
@@ -337,27 +327,6 @@ MgmGlobals::~MgmGlobals(){
delete socketServer;
if (mgmObject)
delete mgmObject;
- if (cluster_config)
- delete cluster_config;
if (interface_name)
free(interface_name);
}
-
-/**
- * @fn readGlobalConfig
- * @param glob : Global variables
- * @return true if success, false otherwise.
- */
-static bool
-readGlobalConfig() {
- if(glob.config_filename == NULL)
- return false;
-
- /* Use config file */
- InitConfigFileParser parser;
- glob.cluster_config = parser.parseConfig(glob.config_filename);
- if(glob.cluster_config == 0){
- return false;
- }
- return true;
-}
diff --git a/ndb/src/ndbapi/NdbConnection.cpp b/ndb/src/ndbapi/NdbConnection.cpp
index 719c5bef49e..f4bb000300a 100644
--- a/ndb/src/ndbapi/NdbConnection.cpp
+++ b/ndb/src/ndbapi/NdbConnection.cpp
@@ -1122,8 +1122,11 @@ NdbConnection::getNdbIndexScanOperation(const NdbIndexImpl* index,
if (indexTable != 0){
NdbIndexScanOperation* tOp =
getNdbScanOperation((NdbTableImpl *) indexTable);
- tOp->m_currentTable = table;
- if(tOp) tOp->m_cursor_type = NdbScanOperation::IndexCursor;
+ if(tOp)
+ {
+ tOp->m_currentTable = table;
+ tOp->m_cursor_type = NdbScanOperation::IndexCursor;
+ }
return tOp;
} else {
setOperationErrorCodeAbort(theNdb->theError.code);
@@ -1618,9 +1621,6 @@ from other transactions.
/**
* There's always a TCKEYCONF when using IgnoreError
*/
-#ifdef VM_TRACE
- ndbout_c("Not completing transaction 2");
-#endif
return -1;
}
/**********************************************************************/
@@ -1872,9 +1872,6 @@ NdbConnection::OpCompleteFailure(Uint8 abortOption, bool setFailure)
/**
* There's always a TCKEYCONF when using IgnoreError
*/
-#ifdef VM_TRACE
- ndbout_c("Not completing transaction");
-#endif
return -1;
}
diff --git a/ndb/src/ndbapi/NdbConnectionScan.cpp b/ndb/src/ndbapi/NdbConnectionScan.cpp
index 3fe8993a42b..a1a220caacf 100644
--- a/ndb/src/ndbapi/NdbConnectionScan.cpp
+++ b/ndb/src/ndbapi/NdbConnectionScan.cpp
@@ -57,12 +57,18 @@ NdbConnection::receiveSCAN_TABREF(NdbApiSignal* aSignal){
if(checkState_TransId(&ref->transId1)){
theScanningOp->theError.code = ref->errorCode;
+ theScanningOp->execCLOSE_SCAN_REP();
if(!ref->closeNeeded){
- theScanningOp->execCLOSE_SCAN_REP();
return 0;
}
- assert(theScanningOp->m_sent_receivers_count);
+
+ /**
+ * Setup so that close_impl will actually perform a close
+ * and not "close scan"-optimze it away
+ */
theScanningOp->m_conf_receivers_count++;
+ theScanningOp->m_conf_receivers[0] = theScanningOp->m_receivers[0];
+ theScanningOp->m_conf_receivers[0]->m_tcPtrI = ~0;
return 0;
} else {
#ifdef NDB_NO_DROPPED_SIGNAL
@@ -97,7 +103,7 @@ NdbConnection::receiveSCAN_TABCONF(NdbApiSignal* aSignal,
theScanningOp->execCLOSE_SCAN_REP();
return 0;
}
-
+
for(Uint32 i = 0; iint2void(ptrI);
assert(tPtr); // For now
NdbReceiver* tOp = theNdb->void2rec(tPtr);
- if (tOp && tOp->checkMagicNumber()){
- if(tOp->execSCANOPCONF(tcPtrI, totalLen, opCount)){
- /**
- *
- */
- theScanningOp->receiver_delivered(tOp);
- } else if(info == ScanTabConf::EndOfData){
+ if (tOp && tOp->checkMagicNumber())
+ {
+ if (tcPtrI == RNIL && opCount == 0)
theScanningOp->receiver_completed(tOp);
- }
- }
- }
- if (conf->requestInfo & ScanTabConf::EndOfData) {
- if(theScanningOp->m_ordered)
- theScanningOp->m_api_receivers_count = 0;
- if(theScanningOp->m_api_receivers_count +
- theScanningOp->m_conf_receivers_count +
- theScanningOp->m_sent_receivers_count){
- abort();
+ else if (tOp->execSCANOPCONF(tcPtrI, totalLen, opCount))
+ theScanningOp->receiver_delivered(tOp);
}
}
return 0;
diff --git a/ndb/src/ndbapi/NdbResultSet.cpp b/ndb/src/ndbapi/NdbResultSet.cpp
index f270584d227..d9d71464026 100644
--- a/ndb/src/ndbapi/NdbResultSet.cpp
+++ b/ndb/src/ndbapi/NdbResultSet.cpp
@@ -44,10 +44,10 @@ void NdbResultSet::init()
{
}
-int NdbResultSet::nextResult(bool fetchAllowed)
+int NdbResultSet::nextResult(bool fetchAllowed, bool forceSend)
{
int res;
- if ((res = m_operation->nextResult(fetchAllowed)) == 0) {
+ if ((res = m_operation->nextResult(fetchAllowed, forceSend)) == 0) {
// handle blobs
NdbBlob* tBlob = m_operation->theBlobList;
while (tBlob != 0) {
@@ -67,9 +67,9 @@ int NdbResultSet::nextResult(bool fetchAllowed)
return res;
}
-void NdbResultSet::close()
+void NdbResultSet::close(bool forceSend)
{
- m_operation->closeScan();
+ m_operation->closeScan(forceSend);
}
NdbOperation*
@@ -98,6 +98,6 @@ NdbResultSet::deleteTuple(NdbConnection * takeOverTrans){
}
int
-NdbResultSet::restart(){
- return m_operation->restart();
+NdbResultSet::restart(bool forceSend){
+ return m_operation->restart(forceSend);
}
diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp
index 4b10ebb10cd..db0c294708d 100644
--- a/ndb/src/ndbapi/NdbScanOperation.cpp
+++ b/ndb/src/ndbapi/NdbScanOperation.cpp
@@ -35,6 +35,8 @@
#include
#include
+#define DEBUG_NEXT_RESULT 0
+
NdbScanOperation::NdbScanOperation(Ndb* aNdb) :
NdbOperation(aNdb),
m_resultSet(0),
@@ -275,6 +277,9 @@ NdbScanOperation::fix_receivers(Uint32 parallel){
void
NdbScanOperation::receiver_delivered(NdbReceiver* tRec){
if(theError.code == 0){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver_delivered");
+
Uint32 idx = tRec->m_list_index;
Uint32 last = m_sent_receivers_count - 1;
if(idx != last){
@@ -298,6 +303,9 @@ NdbScanOperation::receiver_delivered(NdbReceiver* tRec){
void
NdbScanOperation::receiver_completed(NdbReceiver* tRec){
if(theError.code == 0){
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver_completed");
+
Uint32 idx = tRec->m_list_index;
Uint32 last = m_sent_receivers_count - 1;
if(idx != last){
@@ -445,12 +453,12 @@ NdbScanOperation::executeCursor(int nodeId){
return -1;
}
-#define DEBUG_NEXT_RESULT 0
-int NdbScanOperation::nextResult(bool fetchAllowed)
+int NdbScanOperation::nextResult(bool fetchAllowed, bool forceSend)
{
if(m_ordered)
- return ((NdbIndexScanOperation*)this)->next_result_ordered(fetchAllowed);
+ return ((NdbIndexScanOperation*)this)->next_result_ordered(fetchAllowed,
+ forceSend);
/**
* Check current receiver
@@ -487,7 +495,8 @@ int NdbScanOperation::nextResult(bool fetchAllowed)
TransporterFacade* tp = TransporterFacade::instance();
Guard guard(tp->theMutexPtr);
Uint32 seq = theNdbCon->theNodeSequence;
- if(seq == tp->getNodeSequence(nodeId) && send_next_scan(idx, false) == 0){
+ if(seq == tp->getNodeSequence(nodeId) && send_next_scan(idx, false,
+ forceSend) == 0){
idx = m_current_api_receiver;
last = m_api_receivers_count;
@@ -578,8 +587,9 @@ int NdbScanOperation::nextResult(bool fetchAllowed)
}
int
-NdbScanOperation::send_next_scan(Uint32 cnt, bool stopScanFlag){
- if(cnt > 0 || stopScanFlag){
+NdbScanOperation::send_next_scan(Uint32 cnt, bool stopScanFlag,
+ bool forceSend){
+ if(cnt > 0){
NdbApiSignal tSignal(theNdb->theMyRef);
tSignal.setSignal(GSN_SCAN_NEXTREQ);
@@ -595,38 +605,57 @@ NdbScanOperation::send_next_scan(Uint32 cnt, bool stopScanFlag){
*/
Uint32 last = m_sent_receivers_count;
Uint32 * prep_array = (cnt > 21 ? m_prepared_receivers : theData + 4);
+ Uint32 sent = 0;
for(Uint32 i = 0; im_list_index = last+i;
- prep_array[i] = tRec->m_tcPtrI;
- tRec->prepareSend();
+ if((prep_array[sent] = tRec->m_tcPtrI) != RNIL)
+ {
+ m_sent_receivers[last+sent] = tRec;
+ tRec->m_list_index = last+sent;
+ tRec->prepareSend();
+ sent++;
+ }
}
- memcpy(&m_api_receivers[0], &m_api_receivers[cnt], cnt * sizeof(char*));
+ memmove(m_api_receivers, m_api_receivers+cnt,
+ (theParallelism-cnt) * sizeof(char*));
- Uint32 nodeId = theNdbCon->theDBnode;
- TransporterFacade * tp = TransporterFacade::instance();
- int ret;
- if(cnt > 21){
- tSignal.setLength(4);
- LinearSectionPtr ptr[3];
- ptr[0].p = prep_array;
- ptr[0].sz = cnt;
- ret = tp->sendSignal(&tSignal, nodeId, ptr, 1);
- } else {
- tSignal.setLength(4+cnt);
- ret = tp->sendSignal(&tSignal, nodeId);
+ int ret = 0;
+ if(sent)
+ {
+ Uint32 nodeId = theNdbCon->theDBnode;
+ TransporterFacade * tp = TransporterFacade::instance();
+ if(cnt > 21){
+ tSignal.setLength(4);
+ LinearSectionPtr ptr[3];
+ ptr[0].p = prep_array;
+ ptr[0].sz = sent;
+ ret = tp->sendSignal(&tSignal, nodeId, ptr, 1);
+ } else {
+ tSignal.setLength(4+sent);
+ ret = tp->sendSignal(&tSignal, nodeId);
+ }
}
+
+ if (!ret) checkForceSend(forceSend);
- m_sent_receivers_count = last + cnt + stopScanFlag;
+ m_sent_receivers_count = last + sent;
m_api_receivers_count -= cnt;
m_current_api_receiver = 0;
-
+
return ret;
}
return 0;
}
+void NdbScanOperation::checkForceSend(bool forceSend)
+{
+ if (forceSend) {
+ TransporterFacade::instance()->forceSend(theNdb->theNdbBlockNumber);
+ } else {
+ TransporterFacade::instance()->checkForceSend(theNdb->theNdbBlockNumber);
+ }//if
+}
+
int
NdbScanOperation::prepareSend(Uint32 TC_ConnectPtr, Uint64 TransactionId)
{
@@ -642,7 +671,7 @@ NdbScanOperation::doSend(int ProcessorId)
return 0;
}
-void NdbScanOperation::closeScan()
+void NdbScanOperation::closeScan(bool forceSend)
{
if(m_transConnection){
if(DEBUG_NEXT_RESULT)
@@ -657,7 +686,7 @@ void NdbScanOperation::closeScan()
TransporterFacade* tp = TransporterFacade::instance();
Guard guard(tp->theMutexPtr);
- close_impl(tp);
+ close_impl(tp, forceSend);
} while(0);
@@ -673,6 +702,7 @@ NdbScanOperation::execCLOSE_SCAN_REP(){
m_api_receivers_count = 0;
m_conf_receivers_count = 0;
m_sent_receivers_count = 0;
+ m_current_api_receiver = m_ordered ? theParallelism : 0;
}
void NdbScanOperation::release()
@@ -1293,7 +1323,8 @@ NdbIndexScanOperation::compare(Uint32 skip, Uint32 cols,
}
int
-NdbIndexScanOperation::next_result_ordered(bool fetchAllowed){
+NdbIndexScanOperation::next_result_ordered(bool fetchAllowed,
+ bool forceSend){
Uint32 u_idx = 0, u_last = 0;
Uint32 s_idx = m_current_api_receiver; // first sorted
@@ -1319,7 +1350,8 @@ NdbIndexScanOperation::next_result_ordered(bool fetchAllowed){
Guard guard(tp->theMutexPtr);
Uint32 seq = theNdbCon->theNodeSequence;
Uint32 nodeId = theNdbCon->theDBnode;
- if(seq == tp->getNodeSequence(nodeId) && !send_next_scan_ordered(s_idx)){
+ if(seq == tp->getNodeSequence(nodeId) &&
+ !send_next_scan_ordered(s_idx, forceSend)){
Uint32 tmp = m_sent_receivers_count;
s_idx = m_current_api_receiver;
while(m_sent_receivers_count > 0 && !theError.code){
@@ -1408,14 +1440,26 @@ NdbIndexScanOperation::next_result_ordered(bool fetchAllowed){
}
int
-NdbIndexScanOperation::send_next_scan_ordered(Uint32 idx){
+NdbIndexScanOperation::send_next_scan_ordered(Uint32 idx, bool forceSend){
if(idx == theParallelism)
return 0;
+ NdbReceiver* tRec = m_api_receivers[idx];
NdbApiSignal tSignal(theNdb->theMyRef);
tSignal.setSignal(GSN_SCAN_NEXTREQ);
+ Uint32 last = m_sent_receivers_count;
Uint32* theData = tSignal.getDataPtrSend();
+ Uint32* prep_array = theData + 4;
+
+ m_current_api_receiver = idx + 1;
+ if((prep_array[0] = tRec->m_tcPtrI) == RNIL)
+ {
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("receiver completed, don't send");
+ return 0;
+ }
+
theData[0] = theNdbCon->theTCConPtr;
theData[1] = 0;
Uint64 transId = theNdbCon->theTransactionId;
@@ -1425,35 +1469,35 @@ NdbIndexScanOperation::send_next_scan_ordered(Uint32 idx){
/**
* Prepare ops
*/
- Uint32 last = m_sent_receivers_count;
- Uint32 * prep_array = theData + 4;
-
- NdbReceiver * tRec = m_api_receivers[idx];
m_sent_receivers[last] = tRec;
tRec->m_list_index = last;
- prep_array[0] = tRec->m_tcPtrI;
tRec->prepareSend();
-
m_sent_receivers_count = last + 1;
- m_current_api_receiver = idx + 1;
Uint32 nodeId = theNdbCon->theDBnode;
TransporterFacade * tp = TransporterFacade::instance();
tSignal.setLength(4+1);
- return tp->sendSignal(&tSignal, nodeId);
+ int ret= tp->sendSignal(&tSignal, nodeId);
+ if (!ret) checkForceSend(forceSend);
+ return ret;
}
int
-NdbScanOperation::close_impl(TransporterFacade* tp){
+NdbScanOperation::close_impl(TransporterFacade* tp, bool forceSend){
Uint32 seq = theNdbCon->theNodeSequence;
Uint32 nodeId = theNdbCon->theDBnode;
- if(seq != tp->getNodeSequence(nodeId)){
+ if(seq != tp->getNodeSequence(nodeId))
+ {
theNdbCon->theReleaseOnClose = true;
return -1;
}
- while(theError.code == 0 && m_sent_receivers_count){
+ /**
+ * Wait for outstanding
+ */
+ while(theError.code == 0 && m_sent_receivers_count)
+ {
theNdb->theWaiter.m_node = nodeId;
theNdb->theWaiter.m_state = WAIT_SCAN;
int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
@@ -1471,18 +1515,52 @@ NdbScanOperation::close_impl(TransporterFacade* tp){
}
}
- if(m_api_receivers_count+m_conf_receivers_count){
- // Send close scan
- if(send_next_scan(0, true) == -1){ // Close scan
- theNdbCon->theReleaseOnClose = true;
- return -1;
- }
+ /**
+ * move all conf'ed into api
+ * so that send_next_scan can check if they needs to be closed
+ */
+ Uint32 api = m_api_receivers_count;
+ Uint32 conf = m_conf_receivers_count;
+
+ if(m_ordered)
+ {
+ /**
+ * Ordered scan, keep the m_api_receivers "to the right"
+ */
+ memmove(m_api_receivers, m_api_receivers+m_current_api_receiver,
+ (theParallelism - m_current_api_receiver) * sizeof(char*));
+ api = (theParallelism - m_current_api_receiver);
+ m_api_receivers_count = api;
+ }
+
+ if(DEBUG_NEXT_RESULT)
+ ndbout_c("close_impl: [order api conf sent curr parr] %d %d %d %d %d %d",
+ m_ordered, api, conf,
+ m_sent_receivers_count, m_current_api_receiver, theParallelism);
+
+ if(api+conf)
+ {
+ /**
+ * There's something to close
+ * setup m_api_receivers (for send_next_scan)
+ */
+ memcpy(m_api_receivers+api, m_conf_receivers, conf * sizeof(char*));
+ m_api_receivers_count = api + conf;
+ m_conf_receivers_count = 0;
+ }
+
+ // Send close scan
+ if(send_next_scan(api+conf, true, forceSend) == -1)
+ {
+ theNdbCon->theReleaseOnClose = true;
+ return -1;
}
/**
* wait for close scan conf
*/
- while(m_sent_receivers_count+m_api_receivers_count+m_conf_receivers_count){
+ while(m_sent_receivers_count+m_api_receivers_count+m_conf_receivers_count)
+ {
theNdb->theWaiter.m_node = nodeId;
theNdb->theWaiter.m_state = WAIT_SCAN;
int return_code = theNdb->receiveResponse(WAITFOR_SCAN_TIMEOUT);
@@ -1499,6 +1577,7 @@ NdbScanOperation::close_impl(TransporterFacade* tp){
return -1;
}
}
+
return 0;
}
@@ -1520,7 +1599,7 @@ NdbScanOperation::reset_receivers(Uint32 parallell, Uint32 ordered){
}
int
-NdbScanOperation::restart()
+NdbScanOperation::restart(bool forceSend)
{
TransporterFacade* tp = TransporterFacade::instance();
@@ -1529,7 +1608,7 @@ NdbScanOperation::restart()
{
int res;
- if((res= close_impl(tp)))
+ if((res= close_impl(tp, forceSend)))
{
return res;
}
@@ -1548,13 +1627,13 @@ NdbScanOperation::restart()
}
int
-NdbIndexScanOperation::reset_bounds(){
+NdbIndexScanOperation::reset_bounds(bool forceSend){
int res;
{
TransporterFacade* tp = TransporterFacade::instance();
Guard guard(tp->theMutexPtr);
- res= close_impl(tp);
+ res= close_impl(tp, forceSend);
}
if(!res)
diff --git a/ndb/src/ndbapi/ndb_cluster_connection.cpp b/ndb/src/ndbapi/ndb_cluster_connection.cpp
index 4c42fe1aeef..b2043b2c2c1 100644
--- a/ndb/src/ndbapi/ndb_cluster_connection.cpp
+++ b/ndb/src/ndbapi/ndb_cluster_connection.cpp
@@ -45,7 +45,6 @@ Ndb_cluster_connection::Ndb_cluster_connection(const char *connect_string)
else
m_connect_string= 0;
m_config_retriever= 0;
- m_local_config= 0;
m_connect_thread= 0;
m_connect_callback= 0;
@@ -125,38 +124,31 @@ int Ndb_cluster_connection::connect(int reconnect)
do {
if (m_config_retriever == 0)
{
- if (m_local_config == 0) {
- m_local_config= new LocalConfig();
- if (!m_local_config->init(m_connect_string,0)) {
- ndbout_c("Configuration error: Unable to retrieve local config");
- m_local_config->printError();
- m_local_config->printUsage();
- DBUG_RETURN(-1);
- }
- }
m_config_retriever=
- new ConfigRetriever(*m_local_config, NDB_VERSION, NODE_TYPE_API);
+ new ConfigRetriever(m_connect_string, NDB_VERSION, NODE_TYPE_API);
+ if (m_config_retriever->hasError())
+ {
+ printf("Could not connect initialize handle to management server",
+ m_config_retriever->getErrorString());
+ DBUG_RETURN(-1);
+ }
}
else
if (reconnect == 0)
DBUG_RETURN(0);
if (reconnect)
{
- int r= m_config_retriever->do_connect(1);
+ int r= m_config_retriever->do_connect(0,0,0);
if (r == 1)
DBUG_RETURN(1); // mgmt server not up yet
if (r == -1)
break;
}
else
- if(m_config_retriever->do_connect() == -1)
+ if(m_config_retriever->do_connect(12,5,1) == -1)
break;
- Uint32 nodeId = m_config_retriever->allocNodeId();
- for(Uint32 i = 0; nodeId == 0 && i<5; i++){
- NdbSleep_SecSleep(3);
- nodeId = m_config_retriever->allocNodeId();
- }
+ Uint32 nodeId = m_config_retriever->allocNodeId(4/*retries*/,3/*delay*/);
if(nodeId == 0)
break;
ndb_mgm_configuration * props = m_config_retriever->getConfig();
@@ -200,8 +192,6 @@ Ndb_cluster_connection::~Ndb_cluster_connection()
my_free(m_connect_string,MYF(MY_ALLOW_ZERO_PTR));
if (m_config_retriever)
delete m_config_retriever;
- if (m_local_config)
- delete m_local_config;
DBUG_VOID_RETURN;
}
diff --git a/ndb/src/ndbapi/ndberror.c b/ndb/src/ndbapi/ndberror.c
index e08b80f2433..bc49358cc63 100644
--- a/ndb/src/ndbapi/ndberror.c
+++ b/ndb/src/ndbapi/ndberror.c
@@ -426,7 +426,8 @@ ErrorBundle ErrorCodes[] = {
{ 4267, IE, "Corrupted blob value" },
{ 4268, IE, "Error in blob head update forced rollback of transaction" },
{ 4268, IE, "Unknown blob error" },
- { 4269, IE, "No connection to ndb management server" }
+ { 4269, IE, "No connection to ndb management server" },
+ { 4335, AE, "Only one autoincrement column allowed per table. Having a table without primary key uses an autoincremented hidden key, i.e. a table without a primary key can not have an autoincremented column" }
};
static
diff --git a/ndb/test/include/HugoTransactions.hpp b/ndb/test/include/HugoTransactions.hpp
index 19e4cb43336..b833f2ac629 100644
--- a/ndb/test/include/HugoTransactions.hpp
+++ b/ndb/test/include/HugoTransactions.hpp
@@ -36,15 +36,21 @@ public:
bool allowConstraintViolation = true,
int doSleep = 0,
bool oneTrans = false);
+
int scanReadRecords(Ndb*,
int records,
int abort = 0,
int parallelism = 0,
- bool committed = false);
- int scanReadCommittedRecords(Ndb*,
- int records,
- int abort = 0,
- int parallelism = 0);
+ NdbOperation::LockMode = NdbOperation::LM_Read);
+
+ int scanReadRecords(Ndb*,
+ const NdbDictionary::Index*,
+ int records,
+ int abort = 0,
+ int parallelism = 0,
+ NdbOperation::LockMode = NdbOperation::LM_Read,
+ bool sorted = false);
+
int pkReadRecords(Ndb*,
int records,
int batchsize = 1,
diff --git a/ndb/test/include/NdbRestarter.hpp b/ndb/test/include/NdbRestarter.hpp
index 114726f6a2b..19a88b4f8ad 100644
--- a/ndb/test/include/NdbRestarter.hpp
+++ b/ndb/test/include/NdbRestarter.hpp
@@ -87,8 +87,6 @@ protected:
bool connected;
BaseString addr;
- BaseString host;
- int port;
NdbMgmHandle handle;
ndb_mgm_configuration * m_config;
protected:
diff --git a/ndb/test/include/UtilTransactions.hpp b/ndb/test/include/UtilTransactions.hpp
index 37cd99550a5..23902f3b317 100644
--- a/ndb/test/include/UtilTransactions.hpp
+++ b/ndb/test/include/UtilTransactions.hpp
@@ -53,11 +53,11 @@ public:
int selectCount(Ndb*,
int parallelism = 0,
int* count_rows = NULL,
- ScanLock lock = SL_Read,
+ NdbOperation::LockMode lm = NdbOperation::LM_CommittedRead,
NdbConnection* pTrans = NULL);
int scanReadRecords(Ndb*,
int parallelism,
- bool exclusive,
+ NdbOperation::LockMode lm,
int records,
int noAttribs,
int* attrib_list,
diff --git a/ndb/test/ndbapi/testReadPerf.cpp b/ndb/test/ndbapi/testReadPerf.cpp
index 380a809ad00..3adcb5a2d9b 100644
--- a/ndb/test/ndbapi/testReadPerf.cpp
+++ b/ndb/test/ndbapi/testReadPerf.cpp
@@ -391,8 +391,15 @@ run_read(){
void
print_result(){
+ int tmp = 1;
+ tmp *= g_paramters[P_RANGE].value;
+ tmp *= g_paramters[P_LOOPS].value;
+
+ int t, t2;
for(int i = 0; igetTab());
while (iisTestStopped()) {
g_info << i << ": ";
- if (hugoTrans.scanReadCommittedRecords(GETNDB(step), records,
- abort, parallelism) != 0){
+ if (hugoTrans.scanReadRecords(GETNDB(step), records,
+ abort, parallelism,
+ NdbOperation::LM_CommittedRead) != 0){
return NDBT_FAILED;
}
i++;
@@ -639,7 +640,7 @@ int runCheckGetValue(NDBT_Context* ctx, NDBT_Step* step){
g_info << (unsigned)i << endl;
if(utilTrans.scanReadRecords(GETNDB(step),
parallelism,
- false,
+ NdbOperation::LM_Read,
records,
alist.attriblist[i]->numAttribs,
alist.attriblist[i]->attribs) != 0){
@@ -647,7 +648,7 @@ int runCheckGetValue(NDBT_Context* ctx, NDBT_Step* step){
}
if(utilTrans.scanReadRecords(GETNDB(step),
parallelism,
- true,
+ NdbOperation::LM_Read,
records,
alist.attriblist[i]->numAttribs,
alist.attriblist[i]->attribs) != 0){
diff --git a/ndb/test/ndbapi/testScanPerf.cpp b/ndb/test/ndbapi/testScanPerf.cpp
index 003fc67179f..ee2a92e88a9 100644
--- a/ndb/test/ndbapi/testScanPerf.cpp
+++ b/ndb/test/ndbapi/testScanPerf.cpp
@@ -39,8 +39,9 @@ struct Parameter {
#define P_LOOPS 8
#define P_CREATE 9
#define P_LOAD 10
+#define P_RESET 11
-#define P_MAX 11
+#define P_MAX 12
static
Parameter
@@ -55,7 +56,8 @@ g_paramters[] = {
{ "size", 1000000, 1, ~0 },
{ "iterations", 3, 1, ~0 },
{ "create_drop", 1, 0, 1 },
- { "data", 1, 0, 1 }
+ { "data", 1, 0, 1 },
+ { "q-reset bounds", 0, 1, 0 }
};
static Ndb* g_ndb = 0;
@@ -219,21 +221,29 @@ run_scan(){
NDB_TICKS start1, stop;
int sum_time= 0;
+ int sample_rows = 0;
+ NDB_TICKS sample_start = NdbTick_CurrentMillisecond();
+
Uint32 tot = g_paramters[P_ROWS].value;
+ if(g_paramters[P_BOUND].value == 2 || g_paramters[P_FILT].value == 2)
+ iter *= g_paramters[P_ROWS].value;
+
+ NdbScanOperation * pOp = 0;
+ NdbIndexScanOperation * pIOp = 0;
+ NdbConnection * pTrans = 0;
+ NdbResultSet * rs = 0;
+ int check = 0;
+
for(int i = 0; istartTransaction();
+ pTrans = pTrans ? pTrans : g_ndb->startTransaction();
if(!pTrans){
g_err << "Failed to start transaction" << endl;
err(g_ndb->getNdbError());
return -1;
}
- NdbScanOperation * pOp;
- NdbIndexScanOperation * pIOp;
-
- NdbResultSet * rs;
int par = g_paramters[P_PARRA].value;
int bat = g_paramters[P_BATCH].value;
NdbScanOperation::LockMode lm;
@@ -256,9 +266,17 @@ run_scan(){
assert(pOp);
rs = pOp->readTuples(lm, bat, par);
} else {
- pOp = pIOp = pTrans->getNdbIndexScanOperation(g_indexname, g_tablename);
- bool ord = g_paramters[P_ACCESS].value == 2;
- rs = pIOp->readTuples(lm, bat, par, ord);
+ if(g_paramters[P_RESET].value == 0 || pIOp == 0)
+ {
+ pOp= pIOp= pTrans->getNdbIndexScanOperation(g_indexname, g_tablename);
+ bool ord = g_paramters[P_ACCESS].value == 2;
+ rs = pIOp->readTuples(lm, bat, par, ord);
+ }
+ else
+ {
+ pIOp->reset_bounds();
+ }
+
switch(g_paramters[P_BOUND].value){
case 0: // All
break;
@@ -268,20 +286,22 @@ run_scan(){
case 2: { // 1 row
default:
assert(g_table->getNoOfPrimaryKeys() == 1); // only impl. so far
- abort();
-#if 0
int tot = g_paramters[P_ROWS].value;
int row = rand() % tot;
+#if 0
fix_eq_bound(pIOp, row);
+#else
+ pIOp->setBound((Uint32)0, NdbIndexScanOperation::BoundEQ, &row);
#endif
break;
}
}
+ if(g_paramters[P_RESET].value == 1)
+ goto execute;
}
assert(pOp);
assert(rs);
- int check = 0;
switch(g_paramters[P_FILT].value){
case 0: // All
check = pOp->interpret_exit_ok();
@@ -313,7 +333,7 @@ run_scan(){
for(int i = 0; igetNoOfColumns(); i++){
pOp->getValue(i);
}
-
+execute:
int rows = 0;
check = pTrans->execute(NoCommit);
assert(check == 0);
@@ -334,19 +354,29 @@ run_scan(){
return -1;
}
assert(check == 1);
- g_info << "Found " << rows << " rows" << endl;
-
- pTrans->close();
-
+ if(g_paramters[P_RESET].value == 0)
+ {
+ pTrans->close();
+ pTrans = 0;
+ }
stop = NdbTick_CurrentMillisecond();
+
int time_passed= (int)(stop - start1);
- g_err.println("Time: %d ms = %u rows/sec", time_passed,
- (1000*tot)/time_passed);
+ sample_rows += rows;
sum_time+= time_passed;
+
+ if(sample_rows >= tot)
+ {
+ int sample_time = (int)(stop - sample_start);
+ g_info << "Found " << sample_rows << " rows" << endl;
+ g_err.println("Time: %d ms = %u rows/sec", sample_time,
+ (1000*sample_rows)/sample_time);
+ sample_rows = 0;
+ sample_start = stop;
+ }
}
- sum_time= sum_time / iter;
-
- g_err.println("Avg time: %d ms = %u rows/sec", sum_time,
- (1000*tot)/sum_time);
+
+ g_err.println("Avg time: %d ms = %u rows/sec", sum_time/iter,
+ (1000*tot*iter)/sum_time);
return 0;
}
diff --git a/ndb/test/run-test/daily-basic-tests.txt b/ndb/test/run-test/daily-basic-tests.txt
index 8d7e8a06c72..aa38fb4763c 100644
--- a/ndb/test/run-test/daily-basic-tests.txt
+++ b/ndb/test/run-test/daily-basic-tests.txt
@@ -222,6 +222,10 @@ max-time: 500
cmd: testScan
args: -n ScanRead488 -l 10 T6
+max-time: 500
+cmd: testScan
+args: -n ScanRead488Timeout -l 10 T6
+
max-time: 600
cmd: testScan
args: -n ScanRead40 -l 100 T2
diff --git a/ndb/test/run-test/main.cpp b/ndb/test/run-test/main.cpp
index e5f73bc6a5c..ac7710d9546 100644
--- a/ndb/test/run-test/main.cpp
+++ b/ndb/test/run-test/main.cpp
@@ -538,15 +538,19 @@ connect_ndb_mgm(atrt_process & proc){
}
BaseString tmp = proc.m_hostname;
tmp.appfmt(":%d", proc.m_ndb_mgm_port);
- time_t start = time(0);
- const time_t max_connect_time = 30;
- do {
- if(ndb_mgm_connect(handle, tmp.c_str()) != -1){
- proc.m_ndb_mgm_handle = handle;
- return true;
- }
- sleep(1);
- } while(time(0) < (start + max_connect_time));
+
+ if (ndb_mgm_set_connectstring(handle,tmp.c_str()))
+ {
+ g_logger.critical("Unable to create parse connectstring");
+ return false;
+ }
+
+ if(ndb_mgm_connect(handle, 30, 1, 0) != -1)
+ {
+ proc.m_ndb_mgm_handle = handle;
+ return true;
+ }
+
g_logger.critical("Unable to connect to ndb mgm %s", tmp.c_str());
return false;
}
diff --git a/ndb/test/src/HugoTransactions.cpp b/ndb/test/src/HugoTransactions.cpp
index 456bfffbb77..096f5406bbf 100644
--- a/ndb/test/src/HugoTransactions.cpp
+++ b/ndb/test/src/HugoTransactions.cpp
@@ -29,20 +29,13 @@ HugoTransactions::~HugoTransactions(){
deallocRows();
}
-
-int HugoTransactions::scanReadCommittedRecords(Ndb* pNdb,
- int records,
- int abortPercent,
- int parallelism){
- return scanReadRecords(pNdb, records, abortPercent, parallelism, true);
-}
-
int
HugoTransactions::scanReadRecords(Ndb* pNdb,
int records,
int abortPercent,
int parallelism,
- bool committed){
+ NdbOperation::LockMode lm)
+{
int retryAttempt = 0;
const int retryMax = 100;
@@ -80,8 +73,163 @@ HugoTransactions::scanReadRecords(Ndb* pNdb,
}
NdbResultSet * rs;
- rs = pOp ->readTuples(committed ? NdbScanOperation::LM_CommittedRead :
- NdbScanOperation::LM_Read);
+ rs = pOp ->readTuples(lm);
+
+ if( rs == 0 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ check = pOp->interpret_exit_ok();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ for(a = 0; agetValue(tab.getColumn(a)->getName())) == 0) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+ }
+
+ check = pTrans->execute(NoCommit);
+ if( check == -1 ) {
+ const NdbError err = pTrans->getNdbError();
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ // Abort after 1-100 or 1-records rows
+ int ranVal = rand();
+ int abortCount = ranVal % (records == 0 ? 100 : records);
+ bool abortTrans = false;
+ if (abort > 0){
+ // Abort if abortCount is less then abortPercent
+ if (abortCount < abortPercent)
+ abortTrans = true;
+ }
+
+ int eof;
+ int rows = 0;
+ while((eof = rs->nextResult(true)) == 0){
+ rows++;
+ if (calc.verifyRowValues(&row) != 0){
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ if (abortCount == rows && abortTrans == true){
+ ndbout << "Scan is aborted" << endl;
+ g_info << "Scan is aborted" << endl;
+ rs->close();
+ if( check == -1 ) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+ return NDBT_OK;
+ }
+ }
+ if (eof == -1) {
+ const NdbError err = pTrans->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR_INFO(err);
+ pNdb->closeTransaction(pTrans);
+ NdbSleep_MilliSleep(50);
+ switch (err.code){
+ case 488:
+ case 245:
+ case 490:
+ // Too many active scans, no limit on number of retry attempts
+ break;
+ default:
+ retryAttempt++;
+ }
+ continue;
+ }
+ ERR(err);
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pTrans);
+
+ g_info << rows << " rows have been read" << endl;
+ if (records != 0 && rows != records){
+ g_err << "Check expected number of records failed" << endl
+ << " expected=" << records <<", " << endl
+ << " read=" << rows << endl;
+ return NDBT_FAILED;
+ }
+
+ return NDBT_OK;
+ }
+ return NDBT_FAILED;
+}
+
+int
+HugoTransactions::scanReadRecords(Ndb* pNdb,
+ const NdbDictionary::Index * pIdx,
+ int records,
+ int abortPercent,
+ int parallelism,
+ NdbOperation::LockMode lm,
+ bool sorted)
+{
+
+ int retryAttempt = 0;
+ const int retryMax = 100;
+ int check, a;
+ NdbConnection *pTrans;
+ NdbIndexScanOperation *pOp;
+
+ while (true){
+
+ if (retryAttempt >= retryMax){
+ g_err << "ERROR: has retried this operation " << retryAttempt
+ << " times, failing!" << endl;
+ return NDBT_FAILED;
+ }
+
+ pTrans = pNdb->startTransaction();
+ if (pTrans == NULL) {
+ const NdbError err = pNdb->getNdbError();
+
+ if (err.status == NdbError::TemporaryError){
+ ERR(err);
+ NdbSleep_MilliSleep(50);
+ retryAttempt++;
+ continue;
+ }
+ ERR(err);
+ return NDBT_FAILED;
+ }
+
+ pOp = pTrans->getNdbIndexScanOperation(pIdx->getName(), tab.getName());
+ if (pOp == NULL) {
+ ERR(pTrans->getNdbError());
+ pNdb->closeTransaction(pTrans);
+ return NDBT_FAILED;
+ }
+
+ NdbResultSet * rs;
+ rs = pOp ->readTuples(lm, 0, parallelism, sorted);
if( rs == 0 ) {
ERR(pTrans->getNdbError());
diff --git a/ndb/test/src/NDBT_Test.cpp b/ndb/test/src/NDBT_Test.cpp
index 367223f8c98..1434617c988 100644
--- a/ndb/test/src/NDBT_Test.cpp
+++ b/ndb/test/src/NDBT_Test.cpp
@@ -839,9 +839,9 @@ void NDBT_TestSuite::execute(Ndb* ndb, const NdbDictionary::Table* pTab,
continue;
}
pTab2 = pDict->getTable(pTab->getName());
- } else {
+ } else if(!pTab2) {
pTab2 = pTab;
- }
+ }
ctx = new NDBT_Context();
ctx->setTab(pTab2);
diff --git a/ndb/test/src/NdbBackup.cpp b/ndb/test/src/NdbBackup.cpp
index 1ce48d495a5..09f52bf0bed 100644
--- a/ndb/test/src/NdbBackup.cpp
+++ b/ndb/test/src/NdbBackup.cpp
@@ -69,28 +69,19 @@ NdbBackup::getBackupDataDirForNode(int _node_id){
/**
* Fetch configuration from management server
*/
- LocalConfig lc;
- if (!lc.init(0,0)) {
- abort();
- }
- ConfigRetriever cr(lc, 0, NODE_TYPE_API);
- ndb_mgm_configuration * p = 0;
+ ndb_mgm_configuration *p;
+ if (connect())
+ return NULL;
- BaseString tmp; tmp.assfmt("%s:%d", host.c_str(), port);
- NdbMgmHandle handle = ndb_mgm_create_handle();
- if(handle == 0 || ndb_mgm_connect(handle, tmp.c_str()) != 0 ||
- (p = ndb_mgm_get_configuration(handle, 0)) == 0){
-
- const char * s = 0;
- if(p == 0 && handle != 0){
- s = ndb_mgm_get_latest_error_msg(handle);
- if(s == 0)
- s = "No error given!";
+ if ((p = ndb_mgm_get_configuration(handle, 0)) == 0)
+ {
+ const char * s= ndb_mgm_get_latest_error_msg(handle);
+ if(s == 0)
+ s = "No error given!";
- ndbout << "Could not fetch configuration" << endl;
- ndbout << s << endl;
- return NULL;
- }
+ ndbout << "Could not fetch configuration" << endl;
+ ndbout << s << endl;
+ return NULL;
}
/**
@@ -155,13 +146,14 @@ NdbBackup::execRestore(bool _restore_data,
ndbout << "scp res: " << res << endl;
- BaseString::snprintf(buf, 255, "%sndb_restore -c \"host=%s\" -n %d -b %d %s %s .",
+ BaseString::snprintf(buf, 255, "%sndb_restore -c \"%s:%d\" -n %d -b %d %s %s .",
#if 1
"",
#else
"valgrind --leak-check=yes -v "
#endif
- addr.c_str(),
+ ndb_mgm_get_connected_host(handle),
+ ndb_mgm_get_connected_port(handle),
_node_id,
_backup_id,
_restore_data?"-r":"",
diff --git a/ndb/test/src/NdbRestarter.cpp b/ndb/test/src/NdbRestarter.cpp
index 4d6d3ddc001..91c0963feae 100644
--- a/ndb/test/src/NdbRestarter.cpp
+++ b/ndb/test/src/NdbRestarter.cpp
@@ -18,7 +18,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -33,42 +32,11 @@
NdbRestarter::NdbRestarter(const char* _addr):
connected(false),
- port(-1),
handle(NULL),
m_config(0)
{
if (_addr == NULL){
- LocalConfig lcfg;
- if(!lcfg.init()){
- lcfg.printError();
- lcfg.printUsage();
- g_err << "NdbRestarter - Error parsing local config file" << endl;
- return;
- }
-
- if (lcfg.ids.size() == 0){
- g_err << "NdbRestarter - No management servers configured in local config file" << endl;
- return;
- }
-
- for (int i = 0; itype){
- case MgmId_TCP:
- char buf[255];
- snprintf(buf, 255, "%s:%d", m->name.c_str(), m->port);
- addr.assign(buf);
- host.assign(m->name.c_str());
- port = m->port;
- return;
- break;
- case MgmId_File:
- break;
- default:
- break;
- }
- }
+ addr.assign("");
} else {
addr.assign(_addr);
}
@@ -391,13 +359,22 @@ NdbRestarter::isConnected(){
int
NdbRestarter::connect(){
+ disconnect();
handle = ndb_mgm_create_handle();
if (handle == NULL){
g_err << "handle == NULL" << endl;
return -1;
}
g_info << "Connecting to mgmsrv at " << addr.c_str() << endl;
- if (ndb_mgm_connect(handle, addr.c_str()) == -1) {
+ if (ndb_mgm_set_connectstring(handle,addr.c_str()))
+ {
+ MGMERR(handle);
+ g_err << "Connection to " << addr.c_str() << " failed" << endl;
+ return -1;
+ }
+
+ if (ndb_mgm_connect(handle, 0, 0, 0) == -1)
+ {
MGMERR(handle);
g_err << "Connection to " << addr.c_str() << " failed" << endl;
return -1;
diff --git a/ndb/test/src/UtilTransactions.cpp b/ndb/test/src/UtilTransactions.cpp
index c0e6effd244..869f7fc76cb 100644
--- a/ndb/test/src/UtilTransactions.cpp
+++ b/ndb/test/src/UtilTransactions.cpp
@@ -619,7 +619,7 @@ UtilTransactions::addRowToInsert(Ndb* pNdb,
int
UtilTransactions::scanReadRecords(Ndb* pNdb,
int parallelism,
- bool exclusive,
+ NdbOperation::LockMode lm,
int records,
int noAttribs,
int *attrib_list,
@@ -669,10 +669,7 @@ UtilTransactions::scanReadRecords(Ndb* pNdb,
return NDBT_FAILED;
}
- NdbResultSet * rs = pOp->readTuples(exclusive ?
- NdbScanOperation::LM_Exclusive :
- NdbScanOperation::LM_Read,
- 0, parallelism);
+ NdbResultSet * rs = pOp->readTuples(lm, 0, parallelism);
if( rs == 0 ) {
ERR(pTrans->getNdbError());
pNdb->closeTransaction(pTrans);
@@ -761,7 +758,7 @@ int
UtilTransactions::selectCount(Ndb* pNdb,
int parallelism,
int* count_rows,
- ScanLock lock,
+ NdbOperation::LockMode lm,
NdbConnection* pTrans){
int retryAttempt = 0;
@@ -785,19 +782,7 @@ UtilTransactions::selectCount(Ndb* pNdb,
return NDBT_FAILED;
}
- NdbResultSet * rs;
- switch(lock){
- case SL_ReadHold:
- rs = pOp->readTuples(NdbScanOperation::LM_Read);
- break;
- case SL_Exclusive:
- rs = pOp->readTuples(NdbScanOperation::LM_Exclusive);
- break;
- case SL_Read:
- default:
- rs = pOp->readTuples(NdbScanOperation::LM_CommittedRead);
- }
-
+ NdbResultSet * rs = pOp->readTuples(lm);
if( rs == 0) {
ERR(pTrans->getNdbError());
pNdb->closeTransaction(pTrans);
diff --git a/ndb/test/tools/create_index.cpp b/ndb/test/tools/create_index.cpp
index 75a657522f6..6e4c5377f4a 100644
--- a/ndb/test/tools/create_index.cpp
+++ b/ndb/test/tools/create_index.cpp
@@ -30,7 +30,7 @@ main(int argc, const char** argv){
const char* _dbname = "TEST_DB";
int _help = 0;
- int _ordered, _pk;
+ int _ordered = 0, _pk = 1;
struct getargs args[] = {
{ "database", 'd', arg_string, &_dbname, "dbname",
diff --git a/ndb/test/tools/hugoScanRead.cpp b/ndb/test/tools/hugoScanRead.cpp
index cdfdcea4654..42180207a8a 100644
--- a/ndb/test/tools/hugoScanRead.cpp
+++ b/ndb/test/tools/hugoScanRead.cpp
@@ -35,13 +35,17 @@ int main(int argc, const char** argv){
int _parallelism = 1;
const char* _tabname = NULL;
int _help = 0;
-
+ int lock = NdbOperation::LM_Read;
+ int sorted = 0;
+
struct getargs args[] = {
{ "aborts", 'a', arg_integer, &_abort, "percent of transactions that are aborted", "abort%" },
{ "loops", 'l', arg_integer, &_loops, "number of times to run this program(0=infinite loop)", "loops" },
{ "parallelism", 'p', arg_integer, &_parallelism, "parallelism(1-240)", "para" },
{ "records", 'r', arg_integer, &_records, "Number of records", "recs" },
- { "usage", '?', arg_flag, &_help, "Print help", "" }
+ { "usage", '?', arg_flag, &_help, "Print help", "" },
+ { "lock", 'm', arg_integer, &lock, "lock mode", "" },
+ { "sorted", 's', arg_flag, &sorted, "sorted", "" }
};
int num_args = sizeof(args) / sizeof(args[0]);
int optind = 0;
@@ -73,16 +77,48 @@ int main(int argc, const char** argv){
ndbout << " Table " << _tabname << " does not exist!" << endl;
return NDBT_ProgramExit(NDBT_WRONGARGS);
}
+
+ const NdbDictionary::Index * pIdx = 0;
+ if(optind+1 < argc)
+ {
+ pIdx = MyNdb.getDictionary()->getIndex(argv[optind+1], _tabname);
+ if(!pIdx)
+ ndbout << " Index " << argv[optind+1] << " not found" << endl;
+ else
+ if(pIdx->getType() != NdbDictionary::Index::UniqueOrderedIndex &&
+ pIdx->getType() != NdbDictionary::Index::OrderedIndex)
+ {
+ ndbout << " Index " << argv[optind+1] << " is not scannable" << endl;
+ pIdx = 0;
+ }
+ }
HugoTransactions hugoTrans(*pTab);
int i = 0;
while (i<_loops || _loops==0) {
ndbout << i << ": ";
- if(hugoTrans.scanReadRecords(&MyNdb,
- 0,
- _abort,
- _parallelism) != 0){
- return NDBT_ProgramExit(NDBT_FAILED);
+ if(!pIdx)
+ {
+ if(hugoTrans.scanReadRecords(&MyNdb,
+ 0,
+ _abort,
+ _parallelism,
+ (NdbOperation::LockMode)lock) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ }
+ else
+ {
+ if(hugoTrans.scanReadRecords(&MyNdb, pIdx,
+ 0,
+ _abort,
+ _parallelism,
+ (NdbOperation::LockMode)lock,
+ sorted) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
}
i++;
}
diff --git a/ndb/tools/delete_all.cpp b/ndb/tools/delete_all.cpp
index a4fd73a5128..046ac8005d2 100644
--- a/ndb/tools/delete_all.cpp
+++ b/ndb/tools/delete_all.cpp
@@ -67,7 +67,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/desc.cpp b/ndb/tools/desc.cpp
index 8f7a2031ef0..c5e9efdfa8a 100644
--- a/ndb/tools/desc.cpp
+++ b/ndb/tools/desc.cpp
@@ -67,7 +67,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/drop_index.cpp b/ndb/tools/drop_index.cpp
index 1d4b454682f..6600811e0c4 100644
--- a/ndb/tools/drop_index.cpp
+++ b/ndb/tools/drop_index.cpp
@@ -64,7 +64,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/drop_tab.cpp b/ndb/tools/drop_tab.cpp
index 3362c7de47b..0661a8c599b 100644
--- a/ndb/tools/drop_tab.cpp
+++ b/ndb/tools/drop_tab.cpp
@@ -64,7 +64,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/listTables.cpp b/ndb/tools/listTables.cpp
index 05e864a35c4..b923207a4fe 100644
--- a/ndb/tools/listTables.cpp
+++ b/ndb/tools/listTables.cpp
@@ -220,7 +220,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
const char* _tabname;
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/restore/restore_main.cpp b/ndb/tools/restore/restore_main.cpp
index c43791c6723..409ebd54764 100644
--- a/ndb/tools/restore/restore_main.cpp
+++ b/ndb/tools/restore/restore_main.cpp
@@ -143,7 +143,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
bool
readArguments(int *pargc, char*** pargv)
{
- const char *load_default_groups[]= { "ndb_tools","ndb_restore",0 };
+ const char *load_default_groups[]= { "mysql_cluster","ndb_restore",0 };
load_defaults("my",load_default_groups,pargc,pargv);
if (handle_options(pargc, pargv, my_long_options, get_one_option))
{
diff --git a/ndb/tools/select_all.cpp b/ndb/tools/select_all.cpp
index 758c1e48c88..5efeed485a4 100644
--- a/ndb/tools/select_all.cpp
+++ b/ndb/tools/select_all.cpp
@@ -50,7 +50,7 @@ static struct my_option my_long_options[] =
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "parallelism", 'p', "parallelism",
(gptr*) &_parallelism, (gptr*) &_parallelism, 0,
- GET_INT, REQUIRED_ARG, 240, 0, 0, 0, 0, 0 },
+ GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "lock", 'l', "Read(0), Read-hold(1), Exclusive(2)",
(gptr*) &_lock, (gptr*) &_lock, 0,
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
@@ -105,7 +105,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
const char* _tabname;
int ho_error;
@@ -133,13 +133,18 @@ int main(int argc, char** argv){
const NdbDictionary::Table* pTab = NDBT_Table::discoverTableFromDb(&MyNdb, _tabname);
const NdbDictionary::Index * pIdx = 0;
if(argc > 1){
- pIdx = MyNdb.getDictionary()->getIndex(argv[0], _tabname);
+ pIdx = MyNdb.getDictionary()->getIndex(argv[1], _tabname);
}
if(pTab == NULL){
ndbout << " Table " << _tabname << " does not exist!" << endl;
return NDBT_ProgramExit(NDBT_WRONGARGS);
}
+
+ if(argc > 1 && pIdx == 0)
+ {
+ ndbout << " Index " << argv[1] << " does not exists" << endl;
+ }
if(_order && pIdx == NULL){
ndbout << " Order flag given without an index" << endl;
diff --git a/ndb/tools/select_count.cpp b/ndb/tools/select_count.cpp
index 6ee49ddbff0..c3491f842d8 100644
--- a/ndb/tools/select_count.cpp
+++ b/ndb/tools/select_count.cpp
@@ -83,7 +83,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
int ho_error;
if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
diff --git a/ndb/tools/waiter.cpp b/ndb/tools/waiter.cpp
index e24164ea807..5973b046f8f 100644
--- a/ndb/tools/waiter.cpp
+++ b/ndb/tools/waiter.cpp
@@ -23,7 +23,6 @@
#include
#include
#include
-#include
#include
@@ -75,7 +74,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
int main(int argc, char** argv){
NDB_INIT(argv[0]);
- const char *load_default_groups[]= { "ndb_tools",0 };
+ const char *load_default_groups[]= { "mysql_cluster",0 };
load_defaults("my",load_default_groups,&argc,&argv);
const char* _hostName = NULL;
int ho_error;
@@ -85,39 +84,8 @@ int main(int argc, char** argv){
char buf[255];
_hostName = argv[0];
- if (_hostName == NULL){
- LocalConfig lcfg;
- if(!lcfg.init(opt_connect_str, 0))
- {
- lcfg.printError();
- lcfg.printUsage();
- g_err << "Error parsing local config file" << endl;
- return NDBT_ProgramExit(NDBT_FAILED);
- }
-
- for (unsigned i = 0; itype){
- case MgmId_TCP:
- snprintf(buf, 255, "%s:%d", m->name.c_str(), m->port);
- _hostName = buf;
- break;
- case MgmId_File:
- break;
- default:
- break;
- }
- if (_hostName != NULL)
- break;
- }
- if (_hostName == NULL)
- {
- g_err << "No management servers configured in local config file" << endl;
- return NDBT_ProgramExit(NDBT_FAILED);
- }
- }
+ if (_hostName == 0)
+ _hostName= opt_connect_str;
if (_no_contact) {
if (waitClusterStatus(_hostName, NDB_MGM_NODE_STATUS_NO_CONTACT, _timeout) != 0)
@@ -210,13 +178,19 @@ waitClusterStatus(const char* _addr,
int _nodes[MAX_NDB_NODES];
int _num_nodes = 0;
- handle = ndb_mgm_create_handle();
+ handle = ndb_mgm_create_handle();
if (handle == NULL){
g_err << "handle == NULL" << endl;
return -1;
}
g_info << "Connecting to mgmsrv at " << _addr << endl;
- if (ndb_mgm_connect(handle, _addr) == -1) {
+ if (ndb_mgm_set_connectstring(handle, _addr))
+ {
+ MGMERR(handle);
+ g_err << "Connectstring " << _addr << " invalid" << endl;
+ return -1;
+ }
+ if (ndb_mgm_connect(handle,0,0,1)) {
MGMERR(handle);
g_err << "Connection to " << _addr << " failed" << endl;
return -1;
diff --git a/sql/field.cc b/sql/field.cc
index e372a37d2f6..e8669dad406 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1863,9 +1863,9 @@ int Field_long::store(double nr)
res=0;
error= 1;
}
- else if (nr > (double) (ulong) ~0L)
+ else if (nr > (double) UINT_MAX32)
{
- res=(int32) (uint32) ~0L;
+ res= UINT_MAX32;
error= 1;
}
else
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 0dc1399db24..a77d9cf05af 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -1268,7 +1268,7 @@ inline int ha_ndbcluster::next_result(byte *buf)
m_ops_pending= 0;
m_blobs_pending= FALSE;
}
- check= cursor->nextResult(contact_ndb);
+ check= cursor->nextResult(contact_ndb, m_force_send);
if (check == 0)
{
// One more record found
@@ -1561,7 +1561,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
DBUG_ASSERT(op->getSorted() == sorted);
DBUG_ASSERT(op->getLockMode() ==
(NdbOperation::LockMode)get_ndb_lock_type(m_lock.type));
- if(op->reset_bounds())
+ if(op->reset_bounds(m_force_send))
DBUG_RETURN(ndb_err(m_active_trans));
}
@@ -2394,7 +2394,7 @@ int ha_ndbcluster::index_last(byte *buf)
int res;
if((res= ordered_index_scan(0, 0, TRUE, buf)) == 0){
NdbResultSet *cursor= m_active_cursor;
- while((res= cursor->nextResult(TRUE)) == 0);
+ while((res= cursor->nextResult(TRUE, m_force_send)) == 0);
if(res == 1){
unpack_record(buf);
table->status= 0;
@@ -2480,7 +2480,7 @@ int ha_ndbcluster::rnd_init(bool scan)
{
if (!scan)
DBUG_RETURN(1);
- int res= cursor->restart();
+ int res= cursor->restart(m_force_send);
DBUG_ASSERT(res == 0);
}
index_init(table->primary_key);
@@ -2511,7 +2511,7 @@ int ha_ndbcluster::close_scan()
m_ops_pending= 0;
}
- cursor->close();
+ cursor->close(m_force_send);
m_active_cursor= NULL;
DBUG_RETURN(0);
}
@@ -3035,6 +3035,7 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
m_transaction_on= FALSE;
else
m_transaction_on= thd->variables.ndb_use_transactions;
+ // m_use_local_query_cache= thd->variables.ndb_use_local_query_cache;
m_active_trans= thd->transaction.all.ndb_tid ?
(NdbConnection*)thd->transaction.all.ndb_tid:
@@ -3761,7 +3762,8 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
m_ha_not_exact_count(FALSE),
m_force_send(TRUE),
m_autoincrement_prefetch(32),
- m_transaction_on(TRUE)
+ m_transaction_on(TRUE),
+ m_use_local_query_cache(FALSE)
{
int i;
@@ -4449,7 +4451,7 @@ bool ha_ndbcluster::low_byte_first() const
}
bool ha_ndbcluster::has_transactions()
{
- return TRUE;
+ return m_transaction_on;
}
const char* ha_ndbcluster::index_type(uint key_number)
{
@@ -4466,7 +4468,10 @@ const char* ha_ndbcluster::index_type(uint key_number)
}
uint8 ha_ndbcluster::table_cache_type()
{
- return HA_CACHE_TBL_NOCACHE;
+ if (m_use_local_query_cache)
+ return HA_CACHE_TBL_TRANSACT;
+ else
+ return HA_CACHE_TBL_NOCACHE;
}
/*
@@ -4634,13 +4639,12 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
{
DBUG_ENTER("ndb_get_table_statistics");
DBUG_PRINT("enter", ("table: %s", table));
-
+ NdbConnection* pTrans= ndb->startTransaction();
do
{
- NdbConnection* pTrans= ndb->startTransaction();
if (pTrans == NULL)
break;
-
+
NdbScanOperation* pOp= pTrans->getNdbScanOperation(table);
if (pOp == NULL)
break;
@@ -4657,13 +4661,13 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
pOp->getValue(NdbDictionary::Column::ROW_COUNT, (char*)&rows);
pOp->getValue(NdbDictionary::Column::COMMIT_COUNT, (char*)&commits);
- check= pTrans->execute(NoCommit);
+ check= pTrans->execute(NoCommit, AbortOnError, TRUE);
if (check == -1)
break;
Uint64 sum_rows= 0;
Uint64 sum_commits= 0;
- while((check= rs->nextResult(TRUE)) == 0)
+ while((check= rs->nextResult(TRUE, TRUE)) == 0)
{
sum_rows+= rows;
sum_commits+= commits;
@@ -4672,6 +4676,8 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
if (check == -1)
break;
+ rs->close(TRUE);
+
ndb->closeTransaction(pTrans);
if(row_count)
* row_count= sum_rows;
@@ -4681,6 +4687,7 @@ ndb_get_table_statistics(Ndb* ndb, const char * table,
DBUG_RETURN(0);
} while(0);
+ ndb->closeTransaction(pTrans);
DBUG_PRINT("exit", ("failed"));
DBUG_RETURN(-1);
}
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index 354a98e698c..2f18a52b8e9 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -239,10 +239,12 @@ class ha_ndbcluster: public handler
char *m_blobs_buffer;
uint32 m_blobs_buffer_size;
uint m_dupkey;
+ // set from thread variables at external lock
bool m_ha_not_exact_count;
bool m_force_send;
ha_rows m_autoincrement_prefetch;
bool m_transaction_on;
+ bool m_use_local_query_cache;
void set_rec_per_key();
void records_update();
diff --git a/sql/item.cc b/sql/item.cc
index 6ebe34f91dc..f8dc0c6eec8 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -46,12 +46,11 @@ void item_init(void)
}
Item::Item():
- name_length(0), fixed(0),
+ name(0), orig_name(0), name_length(0), fixed(0),
collation(default_charset(), DERIVATION_COERCIBLE)
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
- name= 0;
decimals= 0; max_length= 0;
/* Put item in free list so that we can free all items at end */
@@ -81,6 +80,7 @@ Item::Item():
Item::Item(THD *thd, Item *item):
str_value(item->str_value),
name(item->name),
+ orig_name(item->orig_name),
max_length(item->max_length),
marker(item->marker),
decimals(item->decimals),
@@ -111,10 +111,12 @@ void Item::print_item_w_name(String *str)
void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
- DBUG_PRINT("info", ("Item: 0x%lx", this));
- DBUG_PRINT("info", ("Type: %d", (int)type()));
+ DBUG_PRINT("info", ("Item: 0x%lx, Type: %d, name %s, original name %s",
+ this, (int)type(), name, orig_name));
fixed=0;
marker= 0;
+ if (orig_name)
+ name= orig_name;
DBUG_VOID_RETURN;
}
@@ -135,10 +137,30 @@ bool Item::cleanup_processor(byte *arg)
}
+/*
+ rename item (used for views, cleanup() return original name)
+
+ SYNOPSIS
+ Item::rename()
+ new_name new name of item;
+*/
+
+void Item::rename(char *new_name)
+{
+ /*
+ we can compare pointers to names here, bacause if name was not changed,
+ pointer will be same
+ */
+ if (!orig_name && new_name != name)
+ orig_name= name;
+ name= new_name;
+}
+
+
Item_ident::Item_ident(const char *db_name_par,const char *table_name_par,
const char *field_name_par)
:orig_db_name(db_name_par), orig_table_name(table_name_par),
- orig_field_name(field_name_par),
+ orig_field_name(field_name_par), alias_name_used(FALSE),
db_name(db_name_par), table_name(table_name_par),
field_name(field_name_par), cached_field_index(NO_CACHED_FIELD_INDEX),
cached_table(0), depended_from(0)
@@ -152,6 +174,7 @@ Item_ident::Item_ident(THD *thd, Item_ident *item)
orig_db_name(item->orig_db_name),
orig_table_name(item->orig_table_name),
orig_field_name(item->orig_field_name),
+ alias_name_used(item->alias_name_used),
db_name(item->db_name),
table_name(item->table_name),
field_name(item->field_name),
@@ -609,6 +632,7 @@ void Item_field::set_field(Field *field_par)
table_name=field_par->table_name;
field_name=field_par->field_name;
db_name=field_par->table->table_cache_key;
+ alias_name_used= field_par->table->alias_name_used;
unsigned_flag=test(field_par->flags & UNSIGNED_FLAG);
collation.set(field_par->charset(), DERIVATION_IMPLICIT);
fixed= 1;
@@ -658,7 +682,8 @@ void Item_ident::print(String *str)
THD *thd= current_thd;
char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME];
const char *d_name= db_name, *t_name= table_name;
- if (lower_case_table_names)
+ if (lower_case_table_names== 1 ||
+ (lower_case_table_names == 2 && !alias_name_used))
{
if (table_name && table_name[0])
{
@@ -680,7 +705,7 @@ void Item_ident::print(String *str)
append_identifier(thd, str, nm, strlen(nm));
return;
}
- if (db_name && db_name[0])
+ if (db_name && db_name[0] && !alias_name_used)
{
append_identifier(thd, str, d_name, strlen(d_name));
str->append('.');
@@ -2937,6 +2962,10 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
decimals= (*ref)->decimals;
collation.set((*ref)->collation);
with_sum_func= (*ref)->with_sum_func;
+ if ((*ref)->type() == FIELD_ITEM)
+ alias_name_used= ((Item_ident *) (*ref))->alias_name_used;
+ else
+ alias_name_used= TRUE; // it is not field, so it is was resolved by alias
fixed= 1;
if (ref && (*ref)->check_cols(1))
diff --git a/sql/item.h b/sql/item.h
index 933233e38c3..8f6d6581884 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -139,6 +139,8 @@ public:
*/
String str_value;
my_string name; /* Name from select */
+ /* Original item name (if it was renamed)*/
+ my_string orig_name;
Item *next;
uint32 max_length;
uint name_length; /* Length of name */
@@ -166,6 +168,7 @@ public:
name=0;
} /*lint -e1509 */
void set_name(const char *str,uint length, CHARSET_INFO *cs);
+ void rename(char *new_name);
void init_make_field(Send_field *tmp_field,enum enum_field_types type);
virtual void cleanup();
virtual void make_field(Send_field *field);
@@ -467,6 +470,7 @@ public:
const char *db_name;
const char *table_name;
const char *field_name;
+ bool alias_name_used; /* true if item was resolved against alias */
/*
Cached value of index for this field in table->field array, used by prep.
stmts for speeding up their re-execution. Holds NO_CACHED_FIELD_INDEX
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 02d5f6a1f7a..35ce494257b 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -959,7 +959,8 @@ public:
void update_used_tables();
void print(String *str);
void split_sum_func(THD *thd, Item **ref_pointer_array, List- &fields);
- friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
+ friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
+ COND **conds);
void top_level_item() { abort_on_null=1; }
void copy_andor_arguments(THD *thd, Item_cond *item);
bool walk(Item_processor processor, byte *arg);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 269bd38ffa6..01866a786a6 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1684,18 +1684,21 @@ longlong Item_func_bit_count::val_int()
udf_handler::~udf_handler()
{
- if (initialized)
+ if (!not_original)
{
- if (u_d->func_deinit != NULL)
+ if (initialized)
{
- void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
- u_d->func_deinit;
- (*deinit)(&initid);
+ if (u_d->func_deinit != NULL)
+ {
+ void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
+ u_d->func_deinit;
+ (*deinit)(&initid);
+ }
+ free_udf(u_d);
}
- free_udf(u_d);
+ if (buffers) // Because of bug in ecc
+ delete [] buffers;
}
- if (buffers) // Because of bug in ecc
- delete [] buffers;
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index f8c5c0aac90..304c3ed4bbd 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1422,7 +1422,7 @@ void subselect_uniquesubquery_engine::exclude()
table_map subselect_engine::calc_const_tables(TABLE_LIST *table)
{
table_map map= 0;
- for(; table; table= table->next_local)
+ for(; table; table= table->next_leaf)
{
TABLE *tbl= table->table;
if (tbl && tbl->const_table)
@@ -1435,14 +1435,13 @@ table_map subselect_engine::calc_const_tables(TABLE_LIST *table)
table_map subselect_single_select_engine::upper_select_const_tables()
{
return calc_const_tables((TABLE_LIST *) select_lex->outer_select()->
- table_list.first);
+ leaf_tables);
}
table_map subselect_union_engine::upper_select_const_tables()
{
- return calc_const_tables((TABLE_LIST *) unit->outer_select()->
- table_list.first);
+ return calc_const_tables((TABLE_LIST *) unit->outer_select()->leaf_tables);
}
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 278695b57bb..c1352f7ae7d 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -571,7 +571,7 @@ public:
:Item_sum( list ), udf(udf_arg)
{ quick_group=0;}
Item_udf_sum(THD *thd, Item_udf_sum *item)
- :Item_sum(thd, item), udf(item->udf) {}
+ :Item_sum(thd, item), udf(item->udf) { udf.not_original= TRUE; }
const char *func_name() const { return udf.name(); }
bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index f948bf13226..eb53394b96b 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -623,10 +623,10 @@ bool mysql_drop_index(THD *thd, TABLE_LIST *table_list,
ALTER_INFO *alter_info);
bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
Item **conds, uint order_num, ORDER *order);
-bool mysql_update(THD *thd,TABLE_LIST *tables,List
- &fields,
- List
- &values,COND *conds,
- uint order_num, ORDER *order, ha_rows limit,
- enum enum_duplicates handle_duplicates);
+int mysql_update(THD *thd,TABLE_LIST *tables,List
- &fields,
+ List
- &values,COND *conds,
+ uint order_num, ORDER *order, ha_rows limit,
+ enum enum_duplicates handle_duplicates);
bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
List
- *fields, List
- *values,
COND *conds, ulong options,
@@ -677,7 +677,8 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list,
uint length, Item **ref,
bool check_grants_table, bool check_grants_view,
bool allow_rowid,
- uint *cached_field_index_ptr);
+ uint *cached_field_index_ptr,
+ bool register_tree_change);
Field *
find_field_in_real_table(THD *thd, TABLE *table, const char *name,
uint length, bool check_grants, bool allow_rowid,
@@ -808,14 +809,18 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
bool insert_fields(THD *thd,TABLE_LIST *tables,
const char *db_name, const char *table_name,
List_iterator
- *it, bool any_privileges,
- bool allocate_view_names);
-bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds);
+ bool allocate_view_names,
+ bool select_insert);
+bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds,
+ TABLE_LIST **leaves, bool refresh_only,
+ bool select_insert);
int setup_wild(THD *thd, TABLE_LIST *tables, List
- &fields,
List
- *sum_func_list, uint wild_num);
bool setup_fields(THD *thd, Item** ref_pointer_array, TABLE_LIST *tables,
List
- &item, bool set_query_id,
List
- *sum_func_list, bool allow_sum_func);
-int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
+int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
+ COND **conds);
int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
void wait_for_refresh(THD *thd);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 3001cda8b2b..48b60cc6824 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -5287,7 +5287,7 @@ The minimum value for this variable is 4096.",
(gptr*) &max_system_variables.trans_prealloc_size, 0, GET_ULONG,
REQUIRED_ARG, TRANS_ALLOC_PREALLOC_SIZE, 1024, ~0L, 0, 1024, 0},
{"updatable_views_with_limit", OPT_UPDATABLE_VIEWS_WITH_LIMIT,
- "1 = YES = Don't issue an error message (warning only) if a VIEW without presence of a key of the underlaying table is used in queries with a LIMIT clause for updating. 0 = NO = Prohibit update of a VIEW, which does not contain a key of the underlying table and the query uses a LIMIT clause (usually get from GUI tools).",
+ "1 = YES = Don't issue an error message (warning only) if a VIEW without presence of a key of the underlying table is used in queries with a LIMIT clause for updating. 0 = NO = Prohibit update of a VIEW, which does not contain a key of the underlying table and the query uses a LIMIT clause (usually get from GUI tools).",
(gptr*) &global_system_variables.updatable_views_with_limit,
(gptr*) &max_system_variables.updatable_views_with_limit,
0, GET_ULONG, REQUIRED_ARG, 1, 0, 1, 0, 1, 0},
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index f9a51a8348f..55efcce1c19 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -59,9 +59,9 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond);
SYNOPSIS
opt_sum_query()
- tables Tables in query
- all_fields All fields to be returned
- conds WHERE clause
+ tables list of leaves of join table tree
+ all_fields All fields to be returned
+ conds WHERE clause
NOTE:
This function is only called for queries with sum functions and no
@@ -89,7 +89,7 @@ int opt_sum_query(TABLE_LIST *tables, List
- &all_fields,COND *conds)
where_tables= conds->used_tables();
/* Don't replace expression on a table that is part of an outer join */
- for (TABLE_LIST *tl= tables; tl; tl= tl->next_local)
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf)
{
if (tl->on_expr)
{
@@ -128,7 +128,7 @@ int opt_sum_query(TABLE_LIST *tables, List
- &all_fields,COND *conds)
{
longlong count= 1;
TABLE_LIST *table;
- for (table= tables; table; table= table->next_local)
+ for (table= tables; table; table= table->next_leaf)
{
if (outer_tables || (table->table->file->table_flags() &
HA_NOT_EXACT_COUNT) || table->schema_table)
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 0382e2f95d6..55a8499145a 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -421,3 +421,6 @@ character-set=latin2
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 8b38a3a072e..ee1341cf095 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -412,3 +412,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 995d29ef39e..26ff759ca40 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -421,3 +421,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 4218b2f5b47..8abb684fe11 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -409,3 +409,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 9ef85e60382..1211bd305dd 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -414,3 +414,6 @@ character-set=latin7
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 248c1c8cf9b..5c54b5e01a8 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -409,3 +409,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index b41c0a3bee9..4d1a4a17ebe 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -422,3 +422,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 7887ffa9dd6..146d25cfaee 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -409,3 +409,6 @@ character-set=greek
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 51576bd3bdf..be195b86503 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -414,3 +414,6 @@ character-set=latin2
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 771f0c7ff95..685d2d0c972 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -409,3 +409,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 04cd9f7fb49..665e1991198 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -413,3 +413,6 @@ character-set=ujis
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 2063dc3b275..d4f59b768ff 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -409,3 +409,6 @@ character-set=euckr
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index cb00db472e9..780bfd8d8ca 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -411,3 +411,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index 53e28d71899..d723d0cc475 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -411,3 +411,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index b318c6415b2..b3242c46310 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -414,3 +414,6 @@ character-set=latin2
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index 489a8395b66..b2a993bf002 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -411,3 +411,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index b24ba200a8a..7f5ef856d3a 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -414,3 +414,6 @@ character-set=latin2
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 9d5c708ffcd..74cafac8b52 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -376,7 +376,7 @@ character-set=koi8r
"View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'"
"View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×"
"áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)"
-"ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÏÊ × ÎÅÍ ÔÁÂÌÉÃ(Ù)"
+"ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÙÈ(ÏÊ) × ÎÅÍ ÔÁÂÌÉÃ(Ù)"
"View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ"
"Can't drop a %s from within another stored routine"
"GOTO is not allowed in a stored procedure handler"
@@ -414,3 +414,6 @@ character-set=koi8r
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"ðÒÏ×ÅÒËÁ ËÏÎÔÒÏÌØÎÏÊ ÓÕÍÍÙ ÔÅËÓÔÁ VIEW ÐÒÏ×ÁÌÉÌÁÓØ"
+"îÅÌØÚÑ ÉÚÍÅÎÉÔØ ÂÏÌØÛÅ ÞÅÍ ÏÄÎÕ ÂÁÚÏ×ÕÀ ÔÁÂÌÉÃÕ ÉÓÐÏÌØÚÕÑ ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.64s.%-.64s'"
+"îÅÌØÚÑ ×ÓÔÁ×ÌÑÔØ ÚÁÐÉÓÉ × ÍÎÏÇÏÔÁÂÌÉÞÎÙÊ VIEW '%-.64s.%-.64s' ÂÅÚ ÓÐÉÓËÁ ÐÏÌÅÊ"
+"îÅÌØÚÑ ÕÄÁÌÑÔØ ÉÚ ÍÎÏÇÏÔÁÂÌÉÞÎÏÇÏ VIEW '%-.64s.%-.64s'"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 5a66ab271c6..9a09358a35a 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -402,3 +402,6 @@ character-set=cp1250
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 373ce767f2d..4c694fecc88 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -417,3 +417,6 @@ character-set=latin2
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index 3ba13373c05..94316edd083 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -413,3 +413,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 9a5bbb6d0fc..cd57653969f 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -409,3 +409,6 @@ character-set=latin1
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"View text checksum failed"
+"Can not modify more than one base table through a join view '%-.64s.%-.64s'"
+"Can not insert into join view '%-.64s.%-.64s' without fields list"
+"Can not delete from join view '%-.64s.%-.64s'"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 891798fb3a1..0b3400a4e88 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -415,3 +415,6 @@ character-set=koi8u
"Prepared statement contains too many placeholders"
"Key part '%-.64s' length cannot be 0"
"ðÅÒÅצÒËÁ ËÏÎÔÒÏÌØÎϧ ÓÕÍÉ ÔÅËÓÔÕ VIEW ÎÅ ÐÒÏÊÛÌÁ"
+"îÅÍÏÖÌÉ×Ï ÏÎÏ×ÉÔÉ Â¦ÌØÛ ÎÉÖ ÏÄÎÕ ÂÁÚÏ×Õ ÔÁÂÌÉÃÀ ×ÙËÏÒÉÓÔÏ×ÕÀÞÉ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔ¦ÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
+"îÅÍÏÖÌÉ×Ï ÕÓÔÁ×ÉÔÉ ÒÑÄËÉ Õ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ, ÂÅÚ ÓÐÉÓËÕ ÓÔÏ×Âæ×"
+"îÅÍÏÖÌÉ×Ï ×ÉÄÁÌÉÔÉ ÒÑÄËÉ Õ VIEW '%-.64s.%-.64s', ÝÏ Í¦ÓÔÉÔØ ÄÅ˦ÌØËÁ ÔÁÂÌÉÃØ"
diff --git a/sql/sp.cc b/sql/sp.cc
index a39380d7276..9eff1655711 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -575,6 +575,7 @@ db_show_routine_status(THD *thd, int type, const char *wild)
Item *item;
List
- field_list;
struct st_used_field *used_field;
+ TABLE_LIST *leaves= 0;
st_used_field used_fields[array_elements(init_fields)];
memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields));
@@ -607,7 +608,7 @@ db_show_routine_status(THD *thd, int type, const char *wild)
tables is not VIEW for sure => we can pass 0 as condition
*/
- setup_tables(thd, &tables, 0);
+ setup_tables(thd, &tables, 0, &leaves, FALSE, FALSE);
for (used_field= &used_fields[0];
used_field->field_name;
used_field++)
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 6f966f6b39a..9ce00a01e31 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -2378,7 +2378,7 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (!find_field_in_table(thd, table_list, column->column.ptr(),
column->column.ptr(),
column->column.length(), 0, 0, 0, 0,
- &unused_field_idx))
+ &unused_field_idx, FALSE))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
column->column.c_ptr(), table_list->alias);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0373585af2a..97e2a2cf0d0 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -557,10 +557,10 @@ void close_temporary_tables(THD *thd)
SYNOPSIS
find_table_in_list()
- table Pointer to table list
+ table Pointer to table list
offset Offset to which list in table structure to use
- db_name Data base name
- table_name Table name
+ db_name Data base name
+ table_name Table name
NOTES:
This is called by find_table_in_local_list() and
@@ -580,14 +580,14 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
{
for (; table; table= *(TABLE_LIST **) ((char*) table + offset))
{
- if ((!strcmp(table->db, db_name) &&
- !strcmp(table->real_name, table_name)) ||
- (table->view &&
- table->table->table_cache_key && // it is not temporary table
- !my_strcasecmp(table_alias_charset,
- table->table->table_cache_key, db_name) &&
- !my_strcasecmp(table_alias_charset,
- table->table->table_name, table_name)))
+ if ((table->table == 0 || table->table->tmp_table == NO_TMP_TABLE) &&
+ ((!strcmp(table->db, db_name) &&
+ !strcmp(table->real_name, table_name)) ||
+ (table->view &&
+ !my_strcasecmp(table_alias_charset,
+ table->table->table_cache_key, db_name) &&
+ !my_strcasecmp(table_alias_charset,
+ table->table->table_name, table_name))))
break;
}
}
@@ -595,12 +595,12 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
{
for (; table; table= *(TABLE_LIST **) ((char*) table + offset))
{
- if ((!strcmp(table->db, db_name) &&
- !strcmp(table->real_name, table_name)) ||
- (table->view && // it is VIEW and
- table->table->table_cache_key && // it is not temporary table
- !strcmp(table->table->table_cache_key, db_name) &&
- !strcmp(table->table->table_name, table_name)))
+ if ((table->table == 0 || table->table->tmp_table == NO_TMP_TABLE) &&
+ ((!strcmp(table->db, db_name) &&
+ !strcmp(table->real_name, table_name)) ||
+ (table->view &&
+ !strcmp(table->table->table_cache_key, db_name) &&
+ !strcmp(table->table->table_name, table_name))))
break;
}
}
@@ -1050,8 +1050,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->reginfo.lock_type=TL_READ; /* Assume read */
reset:
+ if (thd->lex->need_correct_ident())
+ table->alias_name_used= my_strcasecmp(table_alias_charset,
+ table->real_name, alias);
/* Fix alias if table name changes */
- if (strcmp(table->table_name,alias))
+ if (strcmp(table->table_name, alias))
{
uint length=(uint) strlen(alias)+1;
table->table_name= (char*) my_realloc(table->table_name,length,
@@ -2072,6 +2075,8 @@ Field *view_ref_found= (Field*) 0x2;
allow_rowid do allow finding of "_rowid" field?
cached_field_index_ptr cached position in field list (used to
speedup prepared tables field finding)
+ register_tree_change TRUE if ref is not stack variable and we
+ need register changes in item tree
RETURN
0 field is not found
@@ -2085,17 +2090,21 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list,
uint length, Item **ref,
bool check_grants_table, bool check_grants_view,
bool allow_rowid,
- uint *cached_field_index_ptr)
+ uint *cached_field_index_ptr,
+ bool register_tree_change)
{
+ DBUG_ENTER("find_field_in_table");
+ DBUG_PRINT("enter", ("table:%s name: %s item name %s, ref 0x%lx",
+ table_list->alias, name, item_name, (ulong)ref));
Field *fld;
if (table_list->field_translation)
{
DBUG_ASSERT(ref != 0 && table_list->view != 0);
uint num= table_list->view->select_lex.item_list.elements;
- Item **trans= table_list->field_translation;
+ Field_translator *trans= table_list->field_translation;
for (uint i= 0; i < num; i ++)
{
- if (strcmp(trans[i]->name, name) == 0)
+ if (strcmp(trans[i].name, name) == 0)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_grants_view &&
@@ -2103,25 +2112,26 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list,
table_list->view_db.str,
table_list->view_name.str,
name, length))
- return WRONG_GRANT;
+ DBUG_RETURN(WRONG_GRANT);
#endif
if (thd->lex->current_select->no_wrap_view_item)
- *ref= trans[i];
+ *ref= trans[i].item;
else
{
- Item_ref *item_ref= new Item_ref(trans + i, table_list->view_name.str,
+ Item_ref *item_ref= new Item_ref(&trans[i].item,
+ table_list->view_name.str,
item_name);
/* as far as Item_ref have defined reference it do not need tables */
- if (item_ref)
+ if (register_tree_change && item_ref)
{
thd->change_item_tree(ref, item_ref);
(*ref)->fix_fields(thd, 0, ref);
}
}
- return (Field*) view_ref_found;
+ DBUG_RETURN((Field*) view_ref_found);
}
}
- return 0;
+ DBUG_RETURN(0);
}
fld= find_field_in_real_table(thd, table_list->table, name, length,
check_grants_table, allow_rowid,
@@ -2135,10 +2145,10 @@ find_field_in_table(THD *thd, TABLE_LIST *table_list,
table_list->view_name.str,
name, length))
{
- return WRONG_GRANT;
+ DBUG_RETURN(WRONG_GRANT);
}
#endif
- return fld;
+ DBUG_RETURN(fld);
}
@@ -2274,17 +2284,34 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
field makes some prepared query ambiguous and so erroneous, but we
accept this trade off.
*/
- found= find_field_in_real_table(thd, item->cached_table->table,
- name, length,
- test(item->cached_table->
- table->grant.want_privilege) &&
- check_privileges,
- 1, &(item->cached_field_index));
+ if (item->cached_table->table)
+ {
+ found= find_field_in_real_table(thd, item->cached_table->table,
+ name, length,
+ test(item->cached_table->
+ table->grant.want_privilege) &&
+ check_privileges,
+ 1, &(item->cached_field_index));
+ }
+ else
+ {
+ TABLE_LIST *table= item->cached_table;
+ Field *find= find_field_in_table(thd, table, name, item->name, length,
+ ref,
+ (table->table &&
+ test(table->table->grant.
+ want_privilege) &&
+ check_privileges),
+ (test(table->grant.want_privilege) &&
+ check_privileges),
+ 1, &(item->cached_field_index),
+ TRUE);
+ }
if (found)
{
if (found == WRONG_GRANT)
- return (Field*) 0;
+ return (Field*) 0;
return found;
}
}
@@ -2312,12 +2339,14 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
found_table=1;
Field *find= find_field_in_table(thd, tables, name, item->name,
length, ref,
- (test(tables->table->grant.
+ (tables->table &&
+ test(tables->table->grant.
want_privilege) &&
check_privileges),
(test(tables->grant.want_privilege) &&
check_privileges),
- 1, &(item->cached_field_index));
+ 1, &(item->cached_field_index),
+ TRUE);
if (find)
{
item->cached_table= tables;
@@ -2368,7 +2397,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
bool allow_rowid= tables && !tables->next_local; // Only one table
for (; tables ; tables= tables->next_local)
{
- if (!tables->table)
+ if (!tables->table && !tables->ancestor)
{
if (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE)
@@ -2378,12 +2407,15 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
Field *field= find_field_in_table(thd, tables, name, item->name,
length, ref,
- (test(tables->table->grant.
+ (tables->table &&
+ test(tables->table->grant.
want_privilege) &&
check_privileges),
(test(tables->grant.want_privilege) &&
check_privileges),
- allow_rowid, &(item->cached_field_index));
+ allow_rowid,
+ &(item->cached_field_index),
+ TRUE);
if (field)
{
if (field == WRONG_GRANT)
@@ -2709,7 +2741,6 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
if (!item->fixed && item->fix_fields(thd, tables, it.ref()) ||
(item= *(it.ref()))->check_cols(1))
{
- select_lex->no_wrap_view_item= 0;
DBUG_RETURN(TRUE); /* purecov: inspected */
}
if (ref)
@@ -2723,40 +2754,97 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
}
+/*
+ make list of leaves of join table tree
+
+ SYNOPSIS
+ make_leaves_list()
+ list pointer to pointer on list first element
+ tables table list
+
+ RETURN pointer on pointer to next_leaf of last element
+*/
+
+TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
+{
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
+ {
+ if (table->view && !table->table)
+ {
+ /* it is for multi table views only, check it */
+ DBUG_ASSERT(table->ancestor->next_local);
+ list= make_leaves_list(list, table->ancestor);
+ }
+ else
+ {
+ *list= table;
+ list= &table->next_leaf;
+ }
+ }
+ return list;
+}
+
/*
prepare tables
SYNOPSIS
setup_tables()
- thd Thread handler
- tables Table list
- conds Condition of current SELECT (can be changed by VIEW)
+ thd Thread handler
+ tables Table list
+ conds Condition of current SELECT (can be changed by VIEW)
+ leaves List of join table leaves list
+ refresh It is onle refresh for subquery
+ select_insert It is SELECT ... INSERT command
NOTE
- Remap table numbers if INSERT ... SELECT
Check also that the 'used keys' and 'ignored keys' exists and set up the
table structure accordingly
+ Create leaf tables list
This has to be called for all tables that are used by items, as otherwise
table->map is not set and all Item_field will be regarded as const items.
RETURN
- 0 ok; In this case *map will includes the choosed index
- 1 error
+ FALSE ok; In this case *map will includes the choosed index
+ TRUE error
*/
-bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds)
+bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds,
+ TABLE_LIST **leaves, bool refresh, bool select_insert)
{
DBUG_ENTER("setup_tables");
+ /*
+ this is used for INSERT ... SELECT.
+ For select we setup tables except first (and its underlying tables)
+ */
+ TABLE_LIST *first_select_table= (select_insert ?
+ tables->next_local:
+ 0);
if (!tables || tables->setup_is_done)
DBUG_RETURN(0);
tables->setup_is_done= 1;
- uint tablenr=0;
- for (TABLE_LIST *table_list= tables;
+
+
+ if (!(*leaves))
+ {
+ make_leaves_list(leaves, tables);
+ }
+
+ uint tablenr= 0;
+ for (TABLE_LIST *table_list= *leaves;
table_list;
- table_list= table_list->next_local, tablenr++)
+ table_list= table_list->next_leaf, tablenr++)
{
TABLE *table= table_list->table;
+ if (first_select_table &&
+ (table_list->belong_to_view ?
+ table_list->belong_to_view :
+ table_list) == first_select_table)
+ {
+ /* new counting for SELECT of INSERT ... SELECT command */
+ first_select_table= 0;
+ tablenr= 0;
+ }
setup_table_map(table, table_list, tablenr);
table->used_keys= table->keys_for_keyread;
if (table_list->use_index)
@@ -2776,16 +2864,24 @@ bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds)
table->keys_in_use_for_query.subtract(map);
}
table->used_keys.intersect(table->keys_in_use_for_query);
- if (table_list->ancestor &&
- table_list->setup_ancestor(thd, conds,
- table_list->effective_with_check))
- DBUG_RETURN(1);
}
if (tablenr > MAX_TABLES)
{
my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES);
DBUG_RETURN(1);
}
+ if (!refresh)
+ {
+ for (TABLE_LIST *table_list= tables;
+ table_list;
+ table_list= table_list->next_local)
+ {
+ if (table_list->ancestor &&
+ table_list->setup_ancestor(thd, conds,
+ table_list->effective_with_check))
+ DBUG_RETURN(1);
+ }
+ }
DBUG_RETURN(0);
}
@@ -2889,9 +2985,12 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
tables->alias) &&
(!db_name || !strcmp(tables->db,db_name))))
{
+ bool view;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Ensure that we have access right to all columns */
- if (!(table->grant.privilege & SELECT_ACL) && !any_privileges)
+ if (!((table && (table->grant.privilege & SELECT_ACL) ||
+ tables->view && (tables->grant.privilege & SELECT_ACL))) &&
+ !any_privileges)
{
if (tables->view)
{
@@ -2904,6 +3003,7 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
}
else if (!tables->schema_table)
{
+ DBUG_ASSERT(table != 0);
table_iter.set(tables);
if (check_grant_all_columns(thd, SELECT_ACL, &table->grant,
table->table_cache_key, table->real_name,
@@ -2912,8 +3012,17 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
}
}
#endif
+ if (table)
+ thd->used_tables|= table->map;
+ else
+ {
+ view_iter.set(tables);
+ for (; !view_iter.end_of_fields(); view_iter.next())
+ {
+ thd->used_tables|= view_iter.item(thd)->used_tables();
+ }
+ }
natural_join_table= 0;
- thd->used_tables|= table->map;
last= embedded= tables;
while ((embedding= embedded->embedding) &&
@@ -2940,9 +3049,15 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
natural_join_table= embedding;
}
if (tables->field_translation)
+ {
iterator= &view_iter;
+ view= 1;
+ }
else
+ {
iterator= &table_iter;
+ view= 0;
+ }
iterator->set(tables);
for (; !iterator->end_of_fields(); iterator->next())
@@ -2955,13 +3070,18 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
!find_field_in_table(thd, natural_join_table, field_name,
field_name,
strlen(field_name), ¬_used_item, 0, 0, 0,
- ¬_used_field_index))
+ ¬_used_field_index, TRUE))
{
Item *item= iterator->item(thd);
if (!found++)
(void) it->replace(item); // Replace '*'
else
it->after(item);
+ if (view && !thd->lex->current_select->no_wrap_view_item)
+ {
+ item= new Item_ref(it->ref(), tables->view_name.str,
+ field_name);
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (any_privileges)
{
@@ -3026,8 +3146,12 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
thd->mem_root);
}
}
- /* All fields are used */
- table->used_fields=table->fields;
+ /*
+ All fields are used in case if usual tables (in case of view used
+ fields marked in setup_tables during fix_fields of view columns
+ */
+ if (table)
+ table->used_fields=table->fields;
}
}
if (found)
@@ -3045,10 +3169,16 @@ err:
/*
-** Fix all conditions and outer join expressions
+ Fix all conditions and outer join expressions
+
+ SYNOPSIS
+ setup_conds()
+ thd thread handler
+ tables list of tables for name resolving
+ leaves list of leaves of join table tree
*/
-int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
+int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds)
{
table_map not_null_tables= 0;
SELECT_LEX *select_lex= thd->lex->current_select;
@@ -3074,7 +3204,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
}
/* Check if we are using outer joins */
- for (table= tables; table; table= table->next_local)
+ for (table= leaves; table; table= table->next_leaf)
{
TABLE_LIST *embedded;
TABLE_LIST *embedding= table;
@@ -3138,8 +3268,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
Field_iterator_view view_iter;
Field_iterator *iterator;
Field *t1_field, *t2_field;
- Item *item_t2;
- Item_cond_and *cond_and=new Item_cond_and();
+ Item *item_t2= 0;
+ Item_cond_and *cond_and= new Item_cond_and();
if (!cond_and) // If not out of memory
goto err_no_arena;
@@ -3165,7 +3295,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
t1_field_name,
strlen(t1_field_name), &item_t2,
0, 0, 0,
- ¬_used_field_index)))
+ ¬_used_field_index,
+ FALSE)))
{
if (t2_field != view_ref_found)
{
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1d2f3d3f62f..083f90fc93e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1650,7 +1650,9 @@ public:
class multi_update :public select_result_interceptor
{
- TABLE_LIST *all_tables, *update_tables, *table_being_updated;
+ TABLE_LIST *all_tables; /* query/update command tables */
+ TABLE_LIST *leaves; /* list of leves of join table tree */
+ TABLE_LIST *update_tables, *table_being_updated;
THD *thd;
TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param;
@@ -1663,8 +1665,9 @@ class multi_update :public select_result_interceptor
bool do_update, trans_safe, transactional_tables, log_delayed;
public:
- multi_update(THD *thd_arg, TABLE_LIST *ut, List
- *fields,
- List
- *values, enum_duplicates handle_duplicates);
+ multi_update(THD *thd_arg, TABLE_LIST *ut, TABLE_LIST *leaves_list,
+ List
- *fields, List
- *values,
+ enum_duplicates handle_duplicates);
~multi_update();
int prepare(List
- &list, SELECT_LEX_UNIT *u);
bool send_data(List
- &items);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index e764e957a16..51cf085cfb5 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -44,7 +44,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE);
- table= table_list->table;
+ if (!(table= table_list->table))
+ {
+ DBUG_ASSERT(table_list->view &&
+ table_list->ancestor && table_list->ancestor->next_local);
+ my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ DBUG_RETURN(-1);
+ }
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
thd->proc_info="init";
table->map=1;
@@ -289,8 +296,9 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_delete");
- if (setup_tables(thd, table_list, conds) ||
- setup_conds(thd, table_list, conds) ||
+ if (setup_tables(thd, table_list, conds, &select_lex->leaf_tables,
+ FALSE, FALSE) ||
+ setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
setup_ftfuncs(select_lex))
DBUG_RETURN(TRUE);
if (!table_list->updatable || check_key_in_view(thd, table_list))
@@ -345,7 +353,8 @@ bool mysql_multi_delete_prepare(THD *thd)
lex->query_tables also point on local list of DELETE SELECT_LEX
*/
- if (setup_tables(thd, lex->query_tables, &lex->select_lex.where))
+ if (setup_tables(thd, lex->query_tables, &lex->select_lex.where,
+ &lex->select_lex.leaf_tables, FALSE, FALSE))
DBUG_RETURN(TRUE);
/* Fix tables-to-be-deleted-from list to point at opened tables */
@@ -365,7 +374,7 @@ bool mysql_multi_delete_prepare(THD *thd)
Check are deleted table used somewhere inside subqueries.
Multi-delete can't be constructed over-union => we always have
- single SELECT on top and have to check underlaying SELECTs of it
+ single SELECT on top and have to check underlying SELECTs of it
*/
for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
un;
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 33bf4c481a2..bcde1dc46f2 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -618,6 +618,7 @@ bool mysqld_help(THD *thd, const char *mask)
st_find_field used_fields[array_elements(init_used_fields)];
DBUG_ENTER("mysqld_help");
+ TABLE_LIST *leaves= 0;
TABLE_LIST tables[4];
bzero((gptr)tables,sizeof(tables));
tables[0].alias= tables[0].real_name= (char*) "help_topic";
@@ -646,7 +647,7 @@ bool mysqld_help(THD *thd, const char *mask)
tables do not contain VIEWs => we can pass 0 as conds
*/
- setup_tables(thd, tables, 0);
+ setup_tables(thd, tables, 0, &leaves, FALSE, FALSE);
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
goto error;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 1d45613dbf4..673d7425c36 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -21,6 +21,7 @@
#include "sql_acl.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_select.h"
static int check_null_fields(THD *thd,TABLE *entry);
#ifndef EMBEDDED_LIBRARY
@@ -31,6 +32,7 @@ static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table);
#endif
+static bool check_view_insertability(TABLE_LIST *view, ulong query_id);
/* Define to force use of my_malloc() if the allocated memory block is big */
@@ -54,8 +56,22 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List
- &fields,
{
TABLE *table= table_list->table;
+ if (!table_list->updatable)
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ return -1;
+ }
+
if (fields.elements == 0 && values.elements != 0)
{
+ if (!table)
+ {
+ DBUG_ASSERT(table_list->view &&
+ table_list->ancestor && table_list->ancestor->next_local);
+ my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ return -1;
+ }
if (values.elements != table->fields)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter);
@@ -93,6 +109,23 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List
- &fields,
thd->lex->select_lex.no_wrap_view_item= 0;
if (res)
return -1;
+ if (table == 0)
+ {
+ /* it is join view => we need to find table for update */
+ List_iterator_fast
- it(fields);
+ Item *item;
+ TABLE_LIST *tbl= 0;
+ table_map map= 0;
+ while (item= it++)
+ map|= item->used_tables();
+ if (table_list->check_single_table(&tbl, map) || tbl == 0)
+ {
+ my_error(ER_VIEW_MULTIUPDATE, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ return -1;
+ }
+ table_list->table= table= tbl->table;
+ }
if (check_unique && thd->dupp_field)
{
@@ -107,6 +140,15 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List
- &fields,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
#endif
+
+ if (check_key_in_view(thd, table_list) ||
+ (table_list->view &&
+ check_view_insertability(table_list, thd->query_id)))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ return -1;
+ }
+
return 0;
}
@@ -131,7 +173,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
ulong counter = 1;
ulonglong id;
COPY_INFO info;
- TABLE *table;
+ TABLE *table= 0;
List_iterator_fast its(values_list);
List_item *values;
#ifndef EMBEDDED_LIBRARY
@@ -197,17 +239,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (res || thd->is_fatal_error)
DBUG_RETURN(TRUE);
- table= table_list->table;
thd->proc_info="init";
thd->used_tables=0;
values= its++;
- if (duplic == DUP_UPDATE && !table->insert_values)
+ if (duplic == DUP_UPDATE)
{
/* it should be allocated before Item::fix_fields() */
- table->insert_values=
- (byte *)alloc_root(thd->mem_root, table->rec_buff_length);
- if (!table->insert_values)
+ if (table_list->set_insert_values(thd->mem_root))
goto abort;
}
@@ -215,6 +254,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
update_fields, update_values, duplic))
goto abort;
+ /* mysql_prepare_insert set table_list->table if it was not set */
+ table= table_list->table;
+
// is table which we are changing used somewhere in other parts of query
value_count= values->elements;
while ((values= its++))
@@ -464,7 +506,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
::send_ok(thd, (ulong) thd->row_count_func, id, buff);
}
free_underlaid_joins(thd, &thd->lex->select_lex);
- table->insert_values=0;
+ table_list->clear_insert_values();
thd->abort_on_warning= 0;
DBUG_RETURN(FALSE);
@@ -474,7 +516,7 @@ abort:
end_delayed_insert(thd);
#endif
free_underlaid_joins(thd, &thd->lex->select_lex);
- table->insert_values=0;
+ table_list->clear_insert_values();
thd->abort_on_warning= 0;
DBUG_RETURN(TRUE);
}
@@ -505,8 +547,9 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
{
uint num= view->view->select_lex.item_list.elements;
TABLE *table= view->table;
- Item **trans_start= view->field_translation, **trans_end=trans_start+num;
- Item **trans;
+ Field_translator *trans_start= view->field_translation,
+ *trans_end= trans_start + num;
+ Field_translator *trans;
Field **field_ptr= table->field;
ulong other_query_id= query_id - 1;
DBUG_ENTER("check_key_in_view");
@@ -519,19 +562,23 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
{
Item_field *field;
/* simple SELECT list entry (field without expression) */
- if (!(field= (*trans)->filed_for_view_update()))
+ if (!(field= trans->item->filed_for_view_update()))
DBUG_RETURN(TRUE);
if (field->field->unireg_check == Field::NEXT_NUMBER)
view->contain_auto_increment= 1;
/* prepare unique test */
field->field->query_id= other_query_id;
- *trans= field; // remove collation if we have it
+ /*
+ remove collation (or other transparent for update function) if we have
+ it
+ */
+ trans->item= field;
}
/* unique test */
for (trans= trans_start; trans != trans_end; trans++)
{
/* Thanks to test above, we know that all columns are of type Item_field */
- Item_field *field= (Item_field *)(*trans);
+ Item_field *field= (Item_field *)trans->item;
if (field->field->query_id == query_id)
DBUG_RETURN(TRUE);
field->field->query_id= query_id;
@@ -550,7 +597,7 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
{
if (trans == trans_end)
DBUG_RETURN(TRUE); // Field was not part of view
- if (((Item_field *)(*trans))->field == *field_ptr)
+ if (((Item_field *)trans->item)->field == *field_ptr)
break; // ok
}
}
@@ -565,39 +612,42 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
SYNOPSIS
mysql_prepare_insert_check_table()
thd Thread handle
- table_list Table list (only one table)
+ table_list Table list
fields List of fields to be updated
where Pointer to where clause
+ select_insert Check is making for SELECT ... INSERT
RETURN
- 0 ok
- 1 ERROR
+ FALSE ok
+ TRUE ERROR
*/
static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
- List
- &fields, COND **where)
+ List
- &fields, COND **where,
+ bool select_insert)
{
bool insert_into_view= (table_list->view != 0);
DBUG_ENTER("mysql_prepare_insert_check_table");
- if (setup_tables(thd, table_list, where))
- DBUG_RETURN(1);
+ if (setup_tables(thd, table_list, where, &thd->lex->select_lex.leaf_tables,
+ FALSE, select_insert))
+ DBUG_RETURN(TRUE);
if (insert_into_view && !fields.elements)
{
thd->lex->empty_field_list_on_rset= 1;
- insert_view_fields(&fields, table_list);
+ if (!table_list->table)
+ {
+ DBUG_ASSERT(table_list->view &&
+ table_list->ancestor && table_list->ancestor->next_local);
+ my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(insert_view_fields(&fields, table_list));
}
- if (!table_list->updatable ||
- check_key_in_view(thd, table_list) ||
- (insert_into_view &&
- check_view_insertability(table_list, thd->query_id)))
- {
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
- DBUG_RETURN(1);
- }
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
}
@@ -625,8 +675,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
bool res;
DBUG_ENTER("mysql_prepare_insert");
- if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds))
- DBUG_RETURN(-1);
+ DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d",
+ (ulong)table_list, (ulong)table,
+ (int)insert_into_view));
+ if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds,
+ FALSE))
+ DBUG_RETURN(TRUE);
if (check_insert_fields(thd, table_list, fields, *values, 1,
!insert_into_view) ||
@@ -639,6 +693,9 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
setup_fields(thd, 0, table_list, update_values, 1, 0, 0))))
DBUG_RETURN(TRUE);
+ if (!table)
+ table= table_list->table;
+
if (unique_table(table_list, table_list->next_global))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
@@ -1654,6 +1711,10 @@ bool delayed_insert::handle_inserts(void)
bool mysql_insert_select_prepare(THD *thd)
{
LEX *lex= thd->lex;
+ TABLE_LIST* first_select_table=
+ (TABLE_LIST*)lex->select_lex.table_list.first;
+ TABLE_LIST* first_select_leaf_table;
+ int res;
DBUG_ENTER("mysql_insert_select_prepare");
/*
SELECT_LEX do not belong to INSERT statement, so we can't add WHERE
@@ -1662,8 +1723,30 @@ bool mysql_insert_select_prepare(THD *thd)
lex->query_tables->no_where_clause= 1;
if (mysql_prepare_insert_check_table(thd, lex->query_tables,
lex->field_list,
- &lex->select_lex.where))
+ &lex->select_lex.where,
+ TRUE))
DBUG_RETURN(TRUE);
+ /*
+ setup was done in mysql_insert_select_prepare, but we have to mark
+ first local table
+ */
+ if (first_select_table)
+ first_select_table->setup_is_done= 1;
+ /*
+ exclude first table from leaf tables list, because it belong to
+ INSERT
+ */
+ DBUG_ASSERT(lex->select_lex.leaf_tables);
+ lex->leaf_tables_insert= lex->select_lex.leaf_tables;
+ /* skip all leaf tables belonged to view where we are insert */
+ for (first_select_leaf_table= lex->select_lex.leaf_tables->next_leaf;
+ first_select_leaf_table &&
+ first_select_leaf_table->belong_to_view &&
+ first_select_leaf_table->belong_to_view ==
+ lex->leaf_tables_insert->belong_to_view;
+ first_select_leaf_table= first_select_leaf_table->next_leaf)
+ {}
+ lex->select_lex.leaf_tables= first_select_leaf_table;
DBUG_RETURN(FALSE);
}
@@ -1692,6 +1775,23 @@ select_insert::prepare(List
- &values, SELECT_LEX_UNIT *u)
if (check_insert_fields(thd, table_list, *fields, values, 1,
!insert_into_view))
DBUG_RETURN(1);
+ /*
+ if it is INSERT into join view then check_insert_fields already found
+ real table for insert
+ */
+ table= table_list->table;
+
+ /*
+ Is table which we are changing used somewhere in other parts of
+ query
+ */
+ if (!(thd->lex->current_select->options & OPTION_BUFFER_RESULT) &&
+ unique_table(table_list, table_list->next_global))
+ {
+ /* Using same table for INSERT and SELECT */
+ thd->lex->current_select->options|= OPTION_BUFFER_RESULT;
+ thd->lex->current_select->join->select_options|= OPTION_BUFFER_RESULT;
+ }
restore_record(table,default_values); // Get empty record
table->next_number_field=table->found_next_number_field;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 971ba125859..63741bcb176 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -150,7 +150,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
lex->time_zone_tables_used= 0;
- lex->proc_table= lex->query_tables= 0;
+ lex->leaf_tables_insert= lex->proc_table= lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
lex->variables_used= 0;
lex->select_lex.parent_lex= lex;
@@ -1048,7 +1048,7 @@ void st_select_lex::init_query()
table_list.empty();
top_join_list.empty();
join_list= &top_join_list;
- embedding= 0;
+ embedding= leaf_tables= 0;
item_list.empty();
join= 0;
where= prep_where= 0;
@@ -1643,7 +1643,7 @@ bool st_lex::can_be_merged()
select_lex.group_list.elements == 0 &&
select_lex.having == 0 &&
select_lex.with_sum_func == 0 &&
- select_lex.table_list.elements == 1 &&
+ select_lex.table_list.elements >= 1 &&
!(select_lex.options & SELECT_DISTINCT) &&
select_lex.select_limit == HA_POS_ERROR);
}
@@ -1723,6 +1723,7 @@ bool st_lex::can_not_use_merged()
TRUE yes, we need only structure
FALSE no, we need data
*/
+
bool st_lex::only_view_structure()
{
switch(sql_command)
@@ -1741,6 +1742,32 @@ bool st_lex::only_view_structure()
}
+/*
+ Should Items_ident be printed correctly
+
+ SYNOPSIS
+ need_correct_ident()
+
+ RETURN
+ TRUE yes, we need only structure
+ FALSE no, we need data
+*/
+
+
+bool st_lex::need_correct_ident()
+{
+ switch(sql_command)
+ {
+ case SQLCOM_SHOW_CREATE:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_CREATE_VIEW:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
/*
initialize limit counters
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 3ae6f91bdd6..605204fe35d 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -447,6 +447,7 @@ public:
List top_join_list; /* join list of the top level */
List *join_list; /* list for the currently parsed join */
TABLE_LIST *embedding; /* table embedding to the above list */
+ TABLE_LIST *leaf_tables; /* list of leaves in join table tree */
const char *type; /* type of select for EXPLAIN */
SQL_LIST order_list; /* ORDER clause */
@@ -669,6 +670,8 @@ typedef struct st_lex
*/
TABLE_LIST **query_tables_last;
TABLE_LIST *proc_table; /* refer to mysql.proc if it was opened by VIEW */
+ /* store original leaf_tables for INSERT SELECT and PS/SP */
+ TABLE_LIST *leaf_tables_insert;
List col_list;
List ref_list;
@@ -707,6 +710,7 @@ typedef struct st_lex
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
uint slave_thd_opt, start_transaction_opt;
+ uint table_count; /* used when usual update transformed in multiupdate */
uint8 describe;
uint8 derived_tables;
uint8 create_view_algorithm;
@@ -758,7 +762,7 @@ typedef struct st_lex
*/
SQL_LIST trg_table_fields;
- st_lex() :result(0)
+ st_lex() :result(0), sql_command(SQLCOM_END)
{
extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first);
hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_spfuns_key, 0, 0);
@@ -802,6 +806,7 @@ typedef struct st_lex
bool can_use_merged();
bool can_not_use_merged();
bool only_view_structure();
+ bool need_correct_ident();
} LEX;
extern TABLE_LIST fake_time_zone_tables_list;
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index d9e4943f322..21dd2318504 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -121,9 +121,12 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table_list->lock_type= lock_type;
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE);
- if (setup_tables(thd, table_list, &unused_conds))
+ if (setup_tables(thd, table_list, &unused_conds,
+ &thd->lex->select_lex.leaf_tables, FALSE, FALSE))
DBUG_RETURN(-1);
- if (!table_list->updatable || check_key_in_view(thd, table_list))
+ if (!table_list->table || // do not suport join view
+ !table_list->updatable || // and derived tables
+ check_key_in_view(thd, table_list))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD");
DBUG_RETURN(TRUE);
@@ -143,8 +146,8 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
thd->dupp_field=0;
/* TODO: use this conds for 'WITH CHECK OPTIONS' */
Item *unused_conds= 0;
- if (setup_tables(thd, table_list, &unused_conds) ||
- setup_fields(thd, 0, table_list, fields, 1, 0, 0))
+ TABLE_LIST *leaves= 0;
+ if (setup_fields(thd, 0, table_list, fields, 1, 0, 0))
DBUG_RETURN(TRUE);
if (thd->dupp_field)
{
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 20fd7fe2ee0..07271d40492 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -153,7 +153,8 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
if (setup_tables(lex->thd, (TABLE_LIST *)select_lex->table_list.first
- &select_lex->where) ||
+ &select_lex->where, &select_lex->leaf_tables,
+ FALSE, FALSE) ||
setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
select_lex->item_list, 1, &all_fields,1) ||
setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 89807765421..fce8d294456 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2030,6 +2030,7 @@ bool
mysql_execute_command(THD *thd)
{
bool res= FALSE;
+ int result= 0;
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
SELECT_LEX *select_lex= &lex->select_lex;
@@ -2874,26 +2875,35 @@ create_error:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables))
break;
- res= mysql_update(thd, all_tables,
- select_lex->item_list,
- lex->value_list,
- select_lex->where,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- select_lex->select_limit,
- lex->duplicates);
- break;
- case SQLCOM_UPDATE_MULTI:
- {
- DBUG_ASSERT(first_table == all_tables && first_table != 0);
- if ((res= multi_update_precheck(thd, all_tables)))
+ res= (result= mysql_update(thd, all_tables,
+ select_lex->item_list,
+ lex->value_list,
+ select_lex->where,
+ select_lex->order_list.elements,
+ (ORDER *) select_lex->order_list.first,
+ select_lex->select_limit,
+ lex->duplicates));
+ /* mysql_update return 2 if we need to switch to multi-update */
+ if (result != 2)
break;
- res= mysql_multi_update(thd, all_tables,
- &select_lex->item_list,
- &lex->value_list,
- select_lex->where,
- select_lex->options,
- lex->duplicates, unit, select_lex);
+ case SQLCOM_UPDATE_MULTI:
+ {
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ /* if we switched from normal update, rights are checked */
+ if (result != 2)
+ {
+ if ((res= multi_update_precheck(thd, all_tables)))
+ break;
+ }
+ else
+ res= 0;
+
+ res= mysql_multi_update(thd, all_tables,
+ &select_lex->item_list,
+ &lex->value_list,
+ select_lex->where,
+ select_lex->options,
+ lex->duplicates, unit, select_lex);
break;
}
case SQLCOM_REPLACE:
@@ -2929,35 +2939,28 @@ create_error:
if (!(res= open_and_lock_tables(thd, all_tables)))
{
- /*
- Is table which we are changing used somewhere in other parts of
- query
- */
- if (unique_table(first_table, all_tables->next_global))
- {
- /* Using same table for INSERT and SELECT */
- select_lex->options |= OPTION_BUFFER_RESULT;
- }
+ /* Skip first table, which is the table we are inserting in */
+ lex->select_lex.table_list.first= (byte*)first_table->next_local;
- if ((res= mysql_insert_select_prepare(thd)))
- break;
- if ((result= new select_insert(first_table, first_table->table,
- &lex->field_list, lex->duplicates,
- lex->duplicates == DUP_IGNORE)))
+ res= mysql_insert_select_prepare(thd);
+ if (!res && (result= new select_insert(first_table, first_table->table,
+ &lex->field_list,
+ lex->duplicates,
+ lex->duplicates == DUP_IGNORE)))
{
- /* Skip first table, which is the table we are inserting in */
- lex->select_lex.table_list.first= (byte*) first_table->next_local;
+ TABLE_LIST *first_select_table;
+
/*
insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
res= handle_select(thd, lex, result);
- /* revert changes for SP */
- lex->select_lex.table_list.first= (byte*) first_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
delete result;
}
+ /* revert changes for SP */
+ lex->select_lex.table_list.first= (byte*) first_table;
}
else
res= TRUE;
@@ -3012,8 +3015,20 @@ create_error:
goto error;
thd->proc_info="init";
- if ((res= open_and_lock_tables(thd, all_tables)) ||
- (res= mysql_multi_delete_prepare(thd)))
+ if ((res= open_and_lock_tables(thd, all_tables)))
+ break;
+
+ if (!first_table->table)
+ {
+ DBUG_ASSERT(first_table->view &&
+ first_table->ancestor && first_table->ancestor->next_local);
+ my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
+ first_table->view_db.str, first_table->view_name.str);
+ res= -1;
+ break;
+ }
+
+ if ((res= mysql_multi_delete_prepare(thd)))
goto error;
if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 06710f6b9fe..d4851393a89 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -935,22 +935,45 @@ error:
tables list of tables queries
RETURN VALUE
- FALSE success
- TRUE error
+ 0 success
+ 2 convert to multi_update
+ 1 error
*/
-static bool mysql_test_update(Prepared_statement *stmt,
+static int mysql_test_update(Prepared_statement *stmt,
TABLE_LIST *table_list)
{
- bool res;
+ int res;
THD *thd= stmt->thd;
+ uint table_count= 0;
SELECT_LEX *select= &stmt->lex->select_lex;
DBUG_ENTER("mysql_test_update");
- if ((res= update_precheck(thd, table_list)))
- DBUG_RETURN(res);
+ if (update_precheck(thd, table_list))
+ DBUG_RETURN(1);
- if (!(res=open_and_lock_tables(thd, table_list)))
+ if (!open_tables(thd, table_list, &table_count))
{
+ if (table_list->ancestor && table_list->ancestor->next_local)
+ {
+ DBUG_ASSERT(table_list->view);
+ DBUG_PRINT("info", ("Switch to multi-update"));
+ /* pass counter value */
+ thd->lex->table_count= table_count;
+ /*
+ give correct value to multi_lock_option, because it will be used
+ in multiupdate
+ */
+ thd->lex->multi_lock_option= table_list->lock_type;
+ /* convert to multiupdate */
+ return 2;
+ }
+
+ if (lock_tables(thd, table_list, table_count) ||
+ mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
+ (thd->fill_derived_tables() &&
+ mysql_handle_derived(thd->lex, &mysql_derived_filling)))
+ DBUG_RETURN(1);
+
if (!(res= mysql_prepare_update(thd, table_list,
&select->where,
select->order_list.elements,
@@ -959,7 +982,7 @@ static bool mysql_test_update(Prepared_statement *stmt,
thd->lex->select_lex.no_wrap_view_item= 1;
if (setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0))
{
- res= -1;
+ res= 1;
thd->lex->select_lex.no_wrap_view_item= 0;
}
else
@@ -967,11 +990,13 @@ static bool mysql_test_update(Prepared_statement *stmt,
thd->lex->select_lex.no_wrap_view_item= 0;
if (setup_fields(thd, 0, table_list,
stmt->lex->value_list, 0, 0, 0))
- res= -1;
+ res= 1;
}
}
stmt->lex->unit.cleanup();
}
+ else
+ res= 1;
/* TODO: here we should send types of placeholders to the client. */
DBUG_RETURN(res);
}
@@ -1001,6 +1026,15 @@ static int mysql_test_delete(Prepared_statement *stmt,
if (!open_and_lock_tables(thd, table_list))
{
+ if (!table_list->table)
+ {
+ DBUG_ASSERT(table_list->view &&
+ table_list->ancestor && table_list->ancestor->next_local);
+ my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ DBUG_RETURN(-1);
+ }
+
mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
lex->unit.cleanup();
DBUG_RETURN(FALSE);
@@ -1195,7 +1229,10 @@ static bool select_like_statement_test(Prepared_statement *stmt,
LEX *lex= stmt->lex;
bool res= 0;
- if (tables && (res= open_and_lock_tables(thd, tables)))
+ /* check that tables was not opened during conversion from usual update */
+ if (tables &&
+ (!tables->table && !tables->view) &&
+ (res= open_and_lock_tables(thd, tables)))
goto end;
if (specific_prepare && (res= (*specific_prepare)(thd)))
@@ -1261,6 +1298,7 @@ static int mysql_test_create_table(Prepared_statement *stmt)
mysql_test_multiupdate()
stmt prepared statemen handler
tables list of tables queries
+ converted converted to multi-update from usual update
RETURN VALUE
FALSE success
@@ -1268,9 +1306,11 @@ static int mysql_test_create_table(Prepared_statement *stmt)
*/
static bool mysql_test_multiupdate(Prepared_statement *stmt,
- TABLE_LIST *tables)
+ TABLE_LIST *tables,
+ bool converted)
{
- if (multi_update_precheck(stmt->thd, tables))
+ /* if we switched from normal update, rights are checked */
+ if (!converted && multi_update_precheck(stmt->thd, tables))
return TRUE;
/*
here we do not pass tables for opening, tables will be opened and locked
@@ -1304,7 +1344,19 @@ static int mysql_test_multidelete(Prepared_statement *stmt,
uint fake_counter;
if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter)))
return res;
- return select_like_statement_test(stmt, tables, &mysql_multi_delete_prepare);
+ if ((res= select_like_statement_test(stmt, tables,
+ &mysql_multi_delete_prepare)))
+ return res;
+ if (!tables->table)
+ {
+ DBUG_ASSERT(tables->view &&
+ tables->ancestor && tables->ancestor->next_local);
+ my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
+ tables->view_db.str, tables->view_name.str);
+ return -1;
+ }
+ return 0;
+
}
@@ -1406,6 +1458,12 @@ static int check_prepared_statement(Prepared_statement *stmt,
case SQLCOM_UPDATE:
res= mysql_test_update(stmt, tables);
+ /* mysql_test_update return 2 if we need to switch to multi-update */
+ if (res != 2)
+ break;
+
+ case SQLCOM_UPDATE_MULTI:
+ res= mysql_test_multiupdate(stmt, tables, res == 2);
break;
case SQLCOM_DELETE:
@@ -1433,10 +1491,6 @@ static int check_prepared_statement(Prepared_statement *stmt,
case SQLCOM_DELETE_MULTI:
res= mysql_test_multidelete(stmt, tables);
break;
-
- case SQLCOM_UPDATE_MULTI:
- res= mysql_test_multiupdate(stmt, tables);
- break;
case SQLCOM_INSERT_SELECT:
case SQLCOM_REPLACE_SELECT:
@@ -1720,8 +1774,15 @@ void reset_stmt_for_execute(THD *thd, LEX *lex)
were closed in the end of previous prepare or execute call.
*/
tables->table= 0;
+ if (tables->nested_join)
+ tables->nested_join->counter= 0;
}
lex->current_select= &lex->select_lex;
+
+ /* restore original list used in INSERT ... SELECT */
+ if (lex->leaf_tables_insert)
+ lex->select_lex.leaf_tables= lex->leaf_tables_insert;
+
if (lex->result)
lex->result->cleanup();
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 4c652ee972a..93ed04be4b2 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -38,7 +38,7 @@ const key_map key_map_empty(0);
const key_map key_map_full(~0);
static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array);
-static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
+static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, COND *conds,
DYNAMIC_ARRAY *keyuse);
static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,
JOIN_TAB *join_tab,
@@ -238,7 +238,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result)
/*
If we have real error reported erly then this will be ignored
*/
- result->send_error(ER_UNKNOWN_ERROR, NullS);
+ result->send_error(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR));
result->abort();
}
DBUG_RETURN(res);
@@ -250,6 +250,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result)
*/
inline int setup_without_group(THD *thd, Item **ref_pointer_array,
TABLE_LIST *tables,
+ TABLE_LIST *leaves,
List
- &fields,
List
- &all_fields,
COND **conds,
@@ -262,7 +263,7 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
save_allow_sum_func= thd->allow_sum_func;
thd->allow_sum_func= 0;
- res= (setup_conds(thd, tables, conds) ||
+ res= (setup_conds(thd, tables, leaves, conds) ||
setup_order(thd, ref_pointer_array, tables, fields, all_fields,
order) ||
setup_group(thd, ref_pointer_array, tables, fields, all_fields,
@@ -309,13 +310,15 @@ JOIN::prepare(Item ***rref_pointer_array,
/* Check that all tables, fields, conds and order are ok */
- if (setup_tables(thd, tables_list, &conds) ||
+ if (setup_tables(thd, tables_list, &conds, &select_lex->leaf_tables,
+ FALSE, FALSE) ||
setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
select_lex->setup_ref_array(thd, og_num) ||
setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1,
&all_fields, 1) ||
- setup_without_group(thd, (*rref_pointer_array), tables_list, fields_list,
- all_fields, &conds, order, group_list,
+ setup_without_group(thd, (*rref_pointer_array), tables_list,
+ select_lex->leaf_tables, fields_list,
+ all_fields, &conds, order, group_list,
&hidden_group_fields))
DBUG_RETURN(-1); /* purecov: inspected */
@@ -383,7 +386,9 @@ JOIN::prepare(Item ***rref_pointer_array,
}
}
TABLE_LIST *table_ptr;
- for (table_ptr= tables_list; table_ptr; table_ptr= table_ptr->next_local)
+ for (table_ptr= select_lex->leaf_tables;
+ table_ptr;
+ table_ptr= table_ptr->next_leaf)
tables++;
}
{
@@ -585,7 +590,7 @@ JOIN::optimize()
opt_sum_query() returns -1 if no rows match to the WHERE conditions,
or 1 if all items were resolved, or 0, or an error number HA_ERR_...
*/
- if ((res=opt_sum_query(tables_list, all_fields, conds)))
+ if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds)))
{
if (res > 1)
{
@@ -611,11 +616,11 @@ JOIN::optimize()
DBUG_RETURN(0);
}
error= -1; // Error is sent to client
- sort_by_table= get_sort_by_table(order, group_list, tables_list);
+ sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables);
/* Calculate how to do the join */
thd->proc_info= "statistics";
- if (make_join_statistics(this, tables_list, conds, &keyuse) ||
+ if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
thd->is_fatal_error)
{
DBUG_PRINT("error",("Error: make_join_statistics() failed"));
@@ -1077,7 +1082,8 @@ JOIN::reinit()
if (tables_list)
{
tables_list->setup_is_done= 0;
- if (setup_tables(thd, tables_list, &conds))
+ if (setup_tables(thd, tables_list, &conds, &select_lex->leaf_tables,
+ TRUE, FALSE))
DBUG_RETURN(1);
}
@@ -1182,7 +1188,7 @@ JOIN::exec()
if (zero_result_cause)
{
- (void) return_zero_rows(this, result, tables_list, fields_list,
+ (void) return_zero_rows(this, result, select_lex->leaf_tables, fields_list,
send_row_on_empty_set(),
select_options,
zero_result_cause,
@@ -2089,7 +2095,7 @@ static ha_rows get_quick_record_count(THD *thd, SQL_SELECT *select,
*/
static bool
-make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
+make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
DYNAMIC_ARRAY *keyuse_array)
{
int error;
@@ -2119,7 +2125,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
for (s= stat, i= 0;
tables;
- s++, tables= tables->next_local, i++)
+ s++, tables= tables->next_leaf, i++)
{
TABLE_LIST *embedding= tables->embedding;
stat_vector[i]=s;
@@ -5074,6 +5080,7 @@ add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
static void
make_outerjoin_info(JOIN *join)
{
+ DBUG_ENTER("make_outerjoin_info");
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
@@ -5117,6 +5124,7 @@ make_outerjoin_info(JOIN *join)
nested_join->first_nested->last_inner= tab;
}
}
+ DBUG_VOID_RETURN;
}
@@ -5219,7 +5227,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
in the ON part of an OUTER JOIN. In this case we want the code
below to check if we should use 'quick' instead.
*/
+ DBUG_PRINT("info", ("Item_int"));
tmp= new Item_int((longlong) 1,1); // Always true
+ DBUG_PRINT("info", ("Item_int 0x%lx", (ulong)tmp));
}
}
@@ -5408,13 +5418,18 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
Now add the guard turning the predicate off for
the null complemented row.
*/
+ DBUG_PRINT("info", ("Item_func_trig_cond"));
tmp= new Item_func_trig_cond(tmp,
&first_inner_tab->not_null_compl);
+ DBUG_PRINT("info", ("Item_func_trig_cond 0x%lx", (ulong) tmp));
if (tmp)
tmp->quick_fix_field();
/* Add the predicate to other pushed down predicates */
+ DBUG_PRINT("info", ("Item_cond_and"));
cond_tab->select_cond= !cond_tab->select_cond ? tmp :
new Item_cond_and(cond_tab->select_cond,tmp);
+ DBUG_PRINT("info", ("Item_cond_and 0x%lx",
+ (ulong)cond_tab->select_cond));
if (!cond_tab->select_cond)
DBUG_RETURN(1);
cond_tab->select_cond->quick_fix_field();
@@ -5967,7 +5982,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
if (send_row)
{
- for (TABLE_LIST *table= tables; table; table= table->next_local)
+ for (TABLE_LIST *table= tables; table; table= table->next_leaf)
mark_as_null_row(table->table); // All fields are NULL
if (having && having->val_int() == 0)
send_row=0;
@@ -8811,6 +8826,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
+ DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond));
if (!select_cond || select_cond->val_int())
{
/*
@@ -11576,7 +11592,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables)
if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT)))
DBUG_RETURN(0);
- for (; !(map & tables->table->map); tables= tables->next_local);
+ for (; !(map & tables->table->map); tables= tables->next_leaf);
if (map != tables->table->map)
DBUG_RETURN(0); // More than one table
DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr));
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index f41eab1ba14..55c38ff37c9 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2909,12 +2909,13 @@ ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx)
0 Can't create table
*/
-TABLE *create_schema_table(THD *thd, ST_SCHEMA_TABLE *schema_table)
+TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
{
int field_count= 0;
Item *item;
TABLE *table;
List
- field_list;
+ ST_SCHEMA_TABLE *schema_table= table_list->schema_table;
ST_FIELD_INFO *fields_info= schema_table->fields_info;
CHARSET_INFO *cs= system_charset_info;
DBUG_ENTER("create_schema_table");
@@ -2959,8 +2960,7 @@ TABLE *create_schema_table(THD *thd, ST_SCHEMA_TABLE *schema_table)
field_list, (ORDER*) 0, 0, 0,
(select_lex->options | thd->options |
TMP_TABLE_ALL_COLUMNS),
- HA_POS_ERROR,
- (char *) schema_table->table_name)))
+ HA_POS_ERROR, table_list->real_name)))
DBUG_RETURN(0);
DBUG_RETURN(table);
}
@@ -3130,8 +3130,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
{
TABLE *table;
DBUG_ENTER("mysql_schema_table");
- if (!(table= table_list->schema_table->
- create_table(thd, table_list->schema_table)))
+ if (!(table= table_list->schema_table->create_table(thd, table_list)))
{
DBUG_RETURN(1);
}
@@ -3292,7 +3291,7 @@ ST_FIELD_INFO columns_fields_info[]=
{"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
{"COLUMN_DEFAULT", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Default"},
{"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
- {"DATA_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
+ {"DATA_TYPE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
{"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
{"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 7b10b80f148..d1f99a6d232 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -56,8 +56,9 @@ class udf_handler :public Sql_alloc
public:
table_map used_tables_cache;
bool const_item_cache;
+ bool not_original;
udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0),
- is_null(0), initialized(0)
+ is_null(0), initialized(0), not_original(0)
{}
~udf_handler();
const char *name() const { return u_d ? u_d->name.str : "?"; }
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index c5167018701..4a2790f7d78 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -86,25 +86,47 @@ static bool check_fields(THD *thd, List
- &items)
}
-bool mysql_update(THD *thd,
- TABLE_LIST *table_list,
- List
- &fields,
- List
- &values,
- COND *conds,
- uint order_num, ORDER *order,
- ha_rows limit,
- enum enum_duplicates handle_duplicates)
+/*
+ Process usual UPDATE
+
+ SYNOPSIS
+ mysql_update()
+ thd thread handler
+ fields fields for update
+ values values of fields for update
+ conds WHERE clause expression
+ order_num number of elemen in ORDER BY clause
+ order ORDER BY clause list
+ limit limit clause
+ handle_duplicates how to handle duplicates
+
+ RETURN
+ 0 - OK
+ 2 - privilege check and openning table passed, but we need to convert to
+ multi-update because of view substitution
+ 1 - error
+*/
+
+int mysql_update(THD *thd,
+ TABLE_LIST *table_list,
+ List
- &fields,
+ List
- &values,
+ COND *conds,
+ uint order_num, ORDER *order,
+ ha_rows limit,
+ enum enum_duplicates handle_duplicates)
{
bool using_limit= limit != HA_POS_ERROR;
bool safe_update= thd->options & OPTION_SAFE_UPDATES;
bool used_key_is_modified, transactional_table, log_delayed;
bool ignore_err= (thd->lex->duplicates == DUP_IGNORE);
- bool res;
+ int res;
int error=0;
uint used_index;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
+ uint table_count= 0;
ulong query_id=thd->query_id, timestamp_query_id;
ha_rows updated, found;
key_map old_used_keys;
@@ -117,8 +139,30 @@ bool mysql_update(THD *thd,
LINT_INIT(used_index);
LINT_INIT(timestamp_query_id);
- if (open_and_lock_tables(thd, table_list))
- DBUG_RETURN(TRUE);
+ if (open_tables(thd, table_list, &table_count))
+ DBUG_RETURN(1);
+
+ if (table_list->ancestor && table_list->ancestor->next_local)
+ {
+ DBUG_ASSERT(table_list->view);
+ DBUG_PRINT("info", ("Switch to multi-update"));
+ /* pass counter value */
+ thd->lex->table_count= table_count;
+ /*
+ give correct value to multi_lock_option, because it will be used
+ in multiupdate
+ */
+ thd->lex->multi_lock_option= table_list->lock_type;
+ /* convert to multiupdate */
+ return 2;
+ }
+
+ if (lock_tables(thd, table_list, table_count) ||
+ mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
+ (thd->fill_derived_tables() &&
+ mysql_handle_derived(thd->lex, &mysql_derived_filling)))
+ DBUG_RETURN(1);
+
thd->proc_info="init";
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -134,7 +178,7 @@ bool mysql_update(THD *thd,
table->grant.want_privilege);
#endif
if (mysql_prepare_update(thd, table_list, &conds, order_num, order))
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
old_used_keys= table->used_keys; // Keys used in WHERE
/*
@@ -156,16 +200,16 @@ bool mysql_update(THD *thd,
res= setup_fields(thd, 0, table_list, fields, 1, 0, 0);
select_lex->no_wrap_view_item= 0;
if (res)
- DBUG_RETURN(TRUE); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
}
if (table_list->view && check_fields(thd, fields))
{
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
if (!table_list->updatable || check_key_in_view(thd, table_list))
{
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
if (table->timestamp_field)
{
@@ -184,7 +228,7 @@ bool mysql_update(THD *thd,
if (setup_fields(thd, 0, table_list, values, 0, 0, 0))
{
free_underlaid_joins(thd, select_lex);
- DBUG_RETURN(TRUE); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
}
// Don't count on usage of 'only index' when calculating which key to use
@@ -197,10 +241,10 @@ bool mysql_update(THD *thd,
free_underlaid_joins(thd, select_lex);
if (error)
{
- DBUG_RETURN(TRUE); // Error in where
+ DBUG_RETURN(1); // Error in where
}
send_ok(thd); // No matching records
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
}
/* If running in safe sql mode, don't allow updates without keys */
if (table->quick_keys.is_clear_all())
@@ -472,7 +516,7 @@ bool mysql_update(THD *thd,
thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */
thd->abort_on_warning= 0;
free_io_cache(table);
- DBUG_RETURN(error >= 0 || thd->net.report_error);
+ DBUG_RETURN((error >= 0 || thd->net.report_error) ? 1 : 0);
err:
delete select;
@@ -483,7 +527,7 @@ err:
table->file->extra(HA_EXTRA_NO_KEYREAD);
}
thd->abort_on_warning= 0;
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(1);
}
/*
@@ -519,8 +563,9 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
tables.table= table;
tables.alias= table_list->alias;
- if (setup_tables(thd, table_list, conds) ||
- setup_conds(thd, table_list, conds) ||
+ if (setup_tables(thd, table_list, conds, &select_lex->leaf_tables,
+ FALSE, FALSE) ||
+ setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
select_lex->setup_ref_array(thd, order_num) ||
setup_order(thd, select_lex->ref_pointer_array,
table_list, all_fields, all_fields, order) ||
@@ -578,42 +623,55 @@ bool mysql_multi_update_prepare(THD *thd)
TABLE_LIST *table_list= lex->query_tables;
List
- *fields= &lex->select_lex.item_list;
TABLE_LIST *tl;
+ TABLE_LIST *leaves;
table_map tables_for_update;
int res;
bool update_view= 0;
- uint table_count;
+ /*
+ if this multi-update was converted from usual update, here is table
+ counter else junk will be assigned here, but then replaced with real
+ count in open_tables()
+ */
+ uint table_count= lex->table_count;
const bool using_lock_tables= thd->locked_tables != 0;
+ bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
+ /* following need for prepared statements, to run next time multi-update */
+ thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
DBUG_ENTER("mysql_multi_update_prepare");
/* open tables and create derived ones, but do not lock and fill them */
- if (open_tables(thd, table_list, & table_count) ||
+ if ((original_multiupdate && open_tables(thd, table_list, & table_count)) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE);
+ /*
+ setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables()
+ second time, but this call will do nothing (there are check for second
+ call in setup_tables()).
+ */
+
+ if (setup_tables(thd, table_list, &lex->select_lex.where,
+ &lex->select_lex.leaf_tables, FALSE, FALSE))
+ DBUG_RETURN(TRUE);
/*
Ensure that we have update privilege for all tables and columns in the
SET part
*/
- for (tl= table_list; tl; tl= tl->next_local)
+ for (tl= (leaves= lex->select_lex.leaf_tables); tl; tl= tl->next_leaf)
{
- TABLE *table= tl->table;
/*
Update of derived tables is checked later
We don't check privileges here, becasue then we would get error
"UPDATE command denided .. for column N" instead of
"Target table ... is not updatable"
*/
- if (!tl->derived)
- tl->grant.want_privilege= table->grant.want_privilege=
+ TABLE *table= tl->table;
+ TABLE_LIST *tlist;
+ if (!(tlist= tl->belong_to_view?tl->belong_to_view:tl)->derived)
+ tlist->grant.want_privilege= table->grant.want_privilege=
(UPDATE_ACL & ~table->grant.privilege);
}
- /*
- setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables()
- second time, but this call will do nothing (there are check for second
- call in setup_tables()).
- */
- if (setup_tables(thd, table_list, &lex->select_lex.where) ||
- (lex->select_lex.no_wrap_view_item= 1,
+ if ((lex->select_lex.no_wrap_view_item= 1,
res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0),
lex->select_lex.no_wrap_view_item= 0,
res))
@@ -638,14 +696,15 @@ bool mysql_multi_update_prepare(THD *thd)
/*
Setup timestamp handling and locking mode
*/
- for (tl= table_list; tl ; tl= tl->next_local)
+ for (tl= leaves; tl; tl= tl->next_leaf)
{
TABLE *table= tl->table;
+ TABLE_LIST *tlist= tl->belong_to_view?tl->belong_to_view:tl;
/* We only need SELECT privilege for columns in the values list */
- tl->grant.want_privilege= table->grant.want_privilege=
+ tlist->grant.want_privilege= table->grant.want_privilege=
(SELECT_ACL & ~table->grant.privilege);
- // Only set timestamp column if this is not modified
+ /* Only set timestamp column if this is not modified */
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
@@ -661,7 +720,7 @@ bool mysql_multi_update_prepare(THD *thd)
/*
Multi-update can't be constructed over-union => we always have
- single SELECT on top and have to check underlaying SELECTs of it
+ single SELECT on top and have to check underlying SELECTs of it
*/
if (lex->select_lex.check_updateable_in_subqueries(tl->db,
tl->real_name))
@@ -683,6 +742,23 @@ bool mysql_multi_update_prepare(THD *thd)
tl->table->reginfo.lock_type= tl->lock_type;
}
+ /* check single table update for view compound from several tables */
+ for (tl= table_list; tl; tl= tl->next_local)
+ {
+ if (tl->table == 0)
+ {
+ DBUG_ASSERT(tl->view &&
+ tl->ancestor && tl->ancestor->next_local);
+ TABLE_LIST *for_update= 0;
+ if (tl->check_single_table(&for_update, tables_for_update))
+ {
+ my_error(ER_VIEW_MULTIUPDATE, MYF(0),
+ tl->view_db.str, tl->view_name.str);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+
opened_tables= thd->status_var.opened_tables;
/* now lock and fill tables */
if (lock_tables(thd, table_list, table_count))
@@ -712,7 +788,8 @@ bool mysql_multi_update_prepare(THD *thd)
/* undone setup_tables() */
table_list->setup_is_done= 0;
- if (setup_tables(thd, table_list, &lex->select_lex.where) ||
+ if (setup_tables(thd, table_list, &lex->select_lex.where,
+ &lex->select_lex.leaf_tables, FALSE, FALSE) ||
(lex->select_lex.no_wrap_view_item= 1,
res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0),
lex->select_lex.no_wrap_view_item= 0,
@@ -739,14 +816,16 @@ bool mysql_multi_update(THD *thd,
enum enum_duplicates handle_duplicates,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
{
- bool res;
+ bool res= FALSE;
multi_update *result;
DBUG_ENTER("mysql_multi_update");
if (mysql_multi_update_prepare(thd))
DBUG_RETURN(TRUE);
- if (!(result= new multi_update(thd, table_list, fields, values,
+ if (!(result= new multi_update(thd, table_list,
+ thd->lex->select_lex.leaf_tables,
+ fields, values,
handle_duplicates)))
DBUG_RETURN(TRUE);
@@ -770,12 +849,14 @@ bool mysql_multi_update(THD *thd,
multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
+ TABLE_LIST *leaves_list,
List
- *field_list, List
- *value_list,
enum enum_duplicates handle_duplicates_arg)
- :all_tables(table_list), update_tables(0), thd(thd_arg), tmp_tables(0),
- updated(0), found(0), fields(field_list), values(value_list),
- table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg),
- do_update(1), trans_safe(0), transactional_tables(1)
+ :all_tables(table_list), leaves(leaves_list), update_tables(0),
+ thd(thd_arg), tmp_tables(0), updated(0), found(0), fields(field_list),
+ values(value_list), table_count(0), copy_field(0),
+ handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0),
+ transactional_tables(1)
{}
@@ -822,8 +903,9 @@ int multi_update::prepare(List
- ¬_used_values,
*/
update.empty();
- for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
+ for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
{
+ /* TODO: add support of view of join support */
TABLE *table=table_ref->table;
if (tables_to_update & table->map)
{
@@ -890,10 +972,10 @@ int multi_update::prepare(List
- ¬_used_values,
which will cause an error when reading a row.
(This issue is mostly relevent for MyISAM tables)
*/
- for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
+ for (table_ref= leaves; table_ref; table_ref= table_ref->next_leaf)
{
TABLE *table=table_ref->table;
- if (!(tables_to_update & table->map) &&
+ if (!(tables_to_update & table->map) &&
find_table_in_local_list(update_tables, table_ref->db,
table_ref->real_name))
table->no_cache= 1; // Disable row cache
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 1b51786fd63..81d8000831b 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -485,17 +485,25 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
{
/* TODO: change here when we will support UNIONs */
for (TABLE_LIST *tbl= (TABLE_LIST *)lex->select_lex.table_list.first;
- tbl;
- tbl= tbl->next_local)
+ tbl;
+ tbl= tbl->next_local)
{
if ((tbl->view && !tbl->updatable_view) || tbl->schema_table)
{
- view->updatable_view= 0;
- break;
+ view->updatable_view= 0;
+ break;
+ }
+ for (TABLE_LIST *up= tbl; up; up= up->embedding)
+ {
+ if (up->outer_join)
+ {
+ view->updatable_view= 0;
+ goto loop_out;
+ }
}
}
}
-
+loop_out:
/*
Check that table of main select do not used in subqueries.
@@ -561,6 +569,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
SELECT_LEX *end;
THD *thd= current_thd;
LEX *old_lex= thd->lex, *lex;
+ SELECT_LEX *view_select;
int res= 0;
/*
@@ -603,7 +612,8 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
*/
table->view= lex= thd->lex= (LEX*) new(thd->mem_root) st_lex_local;
lex_start(thd, (uchar*)table->query.str, table->query.length);
- lex->select_lex.select_number= ++thd->select_number;
+ view_select= &lex->select_lex;
+ view_select->select_number= ++thd->select_number;
old_lex->derived_tables|= DERIVED_VIEW;
{
ulong options= thd->options;
@@ -646,6 +656,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table);
TABLE_LIST *view_tables= lex->query_tables;
TABLE_LIST *view_tables_tail= 0;
+ TABLE_LIST *tbl;
if (lex->spfuns.records)
{
@@ -704,7 +715,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
lex->safe_to_cache_query);
/* move SQL_CACHE to whole query */
- if (lex->select_lex.options & OPTION_TO_QUERY_CACHE)
+ if (view_select->options & OPTION_TO_QUERY_CACHE)
old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
/*
@@ -741,9 +752,6 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
old_lex->can_use_merged()) &&
!old_lex->can_not_use_merged())
{
- /*
- TODO: support multi tables substitutions
- */
/* lex should contain at least one table */
DBUG_ASSERT(view_tables != 0);
@@ -753,20 +761,48 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->effective_with_check= (uint8)table->with_check;
table->ancestor= view_tables;
- /*
- next table should include SELECT_LEX under this table SELECT_LEX
- TODO: here should be loop for multi tables substitution
- */
+ /* next table should include SELECT_LEX under this table SELECT_LEX */
table->ancestor->select_lex= table->select_lex;
+
/*
- move lock type (TODO: should we issue error in case of TMPTABLE
- algorithm and non-read locking)?
+ Process upper level tables of view. As far as we do noy suport union
+ here we can go through local tables of view most upper SELECT
*/
- view_tables->lock_type= table->lock_type;
+ for(tbl= (TABLE_LIST*)view_select->table_list.first;
+ tbl;
+ tbl= tbl->next_local)
+ {
+ /*
+ move lock type (TODO: should we issue error in case of TMPTABLE
+ algorithm and non-read locking)?
+ */
+ tbl->lock_type= table->lock_type;
+ }
+
+ /* multi table view */
+ if (view_tables->next_local)
+ {
+ /* make nested join structure for view tables */
+ NESTED_JOIN *nested_join;
+ if (!(nested_join= table->nested_join=
+ (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ goto err;
+ nested_join->join_list= view_select->top_join_list;
+
+ /* re-nest tables of VIEW */
+ {
+ List_iterator_fast ti(nested_join->join_list);
+ while(tbl= ti++)
+ {
+ tbl->join_list= &nested_join->join_list;
+ tbl->embedding= table;
+ }
+ }
+ }
/* Store WHERE clause for post-processing in setup_ancestor */
- table->where= lex->select_lex.where;
+ table->where= view_select->where;
/*
Add subqueries units to SELECT in which we merging current view.
@@ -793,13 +829,13 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
- lex->select_lex.linkage= DERIVED_TABLE_TYPE;
+ view_select->linkage= DERIVED_TABLE_TYPE;
table->updatable= 0;
table->effective_with_check= VIEW_CHECK_NONE;
/* SELECT tree link */
lex->unit.include_down(table->select_lex);
- lex->unit.slave= &lex->select_lex; // fix include_down initialisation
+ lex->unit.slave= view_select; // fix include_down initialisation
table->derived= &lex->unit;
}
@@ -810,7 +846,7 @@ ok:
if (arena)
thd->restore_backup_item_arena(arena, &backup);
/* global SELECT list linking */
- end= &lex->select_lex; // primary SELECT_LEX is always last
+ end= view_select; // primary SELECT_LEX is always last
end->link_next= old_lex->all_selects_list;
old_lex->all_selects_list->link_prev= &end->link_next;
old_lex->all_selects_list= lex->all_selects_list;
@@ -946,24 +982,26 @@ frm_type_enum mysql_frm_type(char *path)
bool check_key_in_view(THD *thd, TABLE_LIST *view)
{
TABLE *table;
- Item **trans;
+ Field_translator *trans;
KEY *key_info, *key_info_end;
uint i, elements_in_view;
DBUG_ENTER("check_key_in_view");
/*
- we do not support updatable UNIONs in VIW, so we can check just limit of
+ we do not support updatable UNIONs in VIEW, so we can check just limit of
LEX::select_lex
*/
- if (!view->view || thd->lex->sql_command == SQLCOM_INSERT ||
+ if ((!view->view && !view->belong_to_view) || thd->lex->sql_command == SQLCOM_INSERT ||
thd->lex->select_lex.select_limit == HA_POS_ERROR)
DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */
table= view->table;
+ if (view->belong_to_view)
+ view= view->belong_to_view;
trans= view->field_translation;
key_info_end= (key_info= table->key_info)+ table->keys;
elements_in_view= view->view->select_lex.item_list.elements;
- DBUG_ASSERT(view->table != 0 && view->field_translation != 0);
+ DBUG_ASSERT(table != 0 && view->field_translation != 0);
/* Loop over all keys to see if a unique-not-null key is used */
for (;key_info != key_info_end ; key_info++)
@@ -980,7 +1018,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
for (k= 0; k < elements_in_view; k++)
{
Item_field *field;
- if ((field= trans[k]->filed_for_view_update()) &&
+ if ((field= trans[k].item->filed_for_view_update()) &&
field->field == key_part->field)
break;
}
@@ -1001,14 +1039,14 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
for (i= 0; i < elements_in_view; i++)
{
Item_field *field;
- if ((field= trans[i]->filed_for_view_update()) &&
+ if ((field= trans[i].item->filed_for_view_update()) &&
field->field == *field_ptr)
break;
}
if (i == elements_in_view) // If field didn't exists
{
/*
- Keys or all fields of underlaying tables are not foud => we have
+ Keys or all fields of underlying tables are not foud => we have
to check variable updatable_views_with_limit to decide should we
issue an error or just a warning
*/
@@ -1035,24 +1073,33 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
insert_view_fields()
list list for insertion
view view for processing
+
+ RETURN
+ FALSE OK
+ TRUE error (is not sent to cliet)
*/
-void insert_view_fields(List
- *list, TABLE_LIST *view)
+bool insert_view_fields(List
- *list, TABLE_LIST *view)
{
uint elements_in_view= view->view->select_lex.item_list.elements;
- Item **trans;
+ Field_translator *trans;
DBUG_ENTER("insert_view_fields");
if (!(trans= view->field_translation))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(FALSE);
for (uint i= 0; i < elements_in_view; i++)
{
Item_field *fld;
- if ((fld= trans[i]->filed_for_view_update()))
+ if ((fld= trans[i].item->filed_for_view_update()))
list->push_back(fld);
+ else
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), view->alias, "INSERT");
+ DBUG_RETURN(TRUE);
+ }
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(FALSE);
}
/*
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 3248c8819f5..4e6aaf7f477 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -25,7 +25,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode);
bool check_key_in_view(THD *thd, TABLE_LIST * view);
-void insert_view_fields(List
- *list, TABLE_LIST *view);
+bool insert_view_fields(List
- *list, TABLE_LIST *view);
frm_type_enum mysql_frm_type(char *path);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6cb02fa4046..dd9cd4af0f3 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -4837,11 +4837,11 @@ when_list2:
table_ref:
table_factor { $$=$1; }
| join_table { $$=$1; }
- {
+ {
LEX *lex= Lex;
if (!($$= lex->current_select->nest_last_join(lex->thd)))
YYABORT;
- }
+ }
;
join_table_list:
@@ -4931,20 +4931,20 @@ table_factor:
sel->get_use_index(),
sel->get_ignore_index())))
YYABORT;
- sel->add_joined_table($$);
+ sel->add_joined_table($$);
}
| '('
- {
+ {
LEX *lex= Lex;
if (lex->current_select->init_nested_join(lex->thd))
YYABORT;
- }
+ }
join_table_list ')'
{
LEX *lex= Lex;
if (!($$= lex->current_select->end_nested_join(lex->thd)))
YYABORT;
- }
+ }
| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
{ add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
| '(' SELECT_SYM select_derived ')' opt_table_alias
diff --git a/sql/table.cc b/sql/table.cc
index 6702a9acf0a..63575f30326 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -486,7 +486,26 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
/* old frm file */
field_type= (enum_field_types) f_packtype(pack_flag);
- charset=f_is_binary(pack_flag) ? &my_charset_bin : outparam->table_charset;
+ if (f_is_binary(pack_flag))
+ {
+ /*
+ Try to choose the best 4.1 type:
+ - for 4.0 "CHAR(N) BINARY" or "VARCHAR(N) BINARY"
+ try to find a binary collation for character set.
+ - for other types (e.g. BLOB) just use my_charset_bin.
+ */
+ if (!f_is_blob(pack_flag))
+ {
+ // 3.23 or 4.0 string
+ if (!(charset= get_charset_by_csname(outparam->table_charset->csname,
+ MY_CS_BINSORT, MYF(0))))
+ charset= &my_charset_bin;
+ }
+ else
+ charset= &my_charset_bin;
+ }
+ else
+ charset= outparam->table_charset;
bzero((char*) &comment, sizeof(comment));
}
*field_ptr=reg_field=
@@ -1517,11 +1536,68 @@ void st_table_list::calc_md5(char *buffer)
void st_table_list::set_ancestor()
{
- if (ancestor->ancestor)
- ancestor->set_ancestor();
- table= ancestor->table;
- schema_table= ancestor->schema_table;
- ancestor->table->grant= grant;
+ /* process all tables of view */
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->ancestor)
+ ancestor->set_ancestor();
+ tbl->table->grant= grant;
+ }
+ /* if view contain only one table, substitute TABLE of it */
+ if (!ancestor->next_local)
+ {
+ table= ancestor->table;
+ schema_table= ancestor->schema_table;
+ }
+}
+
+
+/*
+ Save old want_privilege and clear want_privilege
+
+ SYNOPSIS
+ save_and_clear_want_privilege()
+*/
+
+void st_table_list::save_and_clear_want_privilege()
+{
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->table)
+ {
+ privilege_backup= tbl->table->grant.want_privilege;
+ tbl->table->grant.want_privilege= 0;
+ }
+ else
+ {
+ DBUG_ASSERT(tbl->view && tbl->ancestor &&
+ tbl->ancestor->next_local);
+ tbl->save_and_clear_want_privilege();
+ }
+ }
+}
+
+
+/*
+ restore want_privilege saved by save_and_clear_want_privilege
+
+ SYNOPSIS
+ restore_want_privilege()
+*/
+
+void st_table_list::restore_want_privilege()
+{
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->table)
+ tbl->table->grant.want_privilege= privilege_backup;
+ else
+ {
+ DBUG_ASSERT(tbl->view && tbl->ancestor &&
+ tbl->ancestor->next_local);
+ tbl->restore_want_privilege();
+ }
+ }
}
@@ -1551,10 +1627,11 @@ void st_table_list::set_ancestor()
bool st_table_list::setup_ancestor(THD *thd, Item **conds,
uint8 check_opt_type)
{
- Item **transl;
+ Field_translator *transl;
SELECT_LEX *select= &view->select_lex;
SELECT_LEX *current_select_save= thd->lex->current_select;
Item *item;
+ TABLE_LIST *tbl;
List_iterator_fast
- it(select->item_list);
uint i= 0;
bool save_set_query_id= thd->set_query_id;
@@ -1562,38 +1639,57 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds,
bool save_allow_sum_func= thd->allow_sum_func;
DBUG_ENTER("st_table_list::setup_ancestor");
- if (ancestor->ancestor &&
- ancestor->setup_ancestor(thd, conds,
- (check_opt_type == VIEW_CHECK_CASCADED ?
- VIEW_CHECK_CASCADED :
- VIEW_CHECK_NONE)))
- DBUG_RETURN(1);
+ for (tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->ancestor &&
+ tbl->setup_ancestor(thd, conds,
+ (check_opt_type == VIEW_CHECK_CASCADED ?
+ VIEW_CHECK_CASCADED :
+ VIEW_CHECK_NONE)))
+ DBUG_RETURN(1);
+ }
if (field_translation)
{
/* prevent look up in SELECTs tree */
thd->lex->current_select= &thd->lex->select_lex;
+ thd->lex->select_lex.no_wrap_view_item= 1;
thd->set_query_id= 1;
/* this view was prepared already on previous PS/SP execution */
- Item **end= field_translation + select->item_list.elements;
- for (Item **item= field_translation; item < end; item++)
+ Field_translator *end= field_translation + select->item_list.elements;
+ /* real rights will be checked in VIEW field */
+ save_and_clear_want_privilege();
+ /* aggregate function are allowed */
+ thd->allow_sum_func= 1;
+ for (transl= field_translation; transl < end; transl++)
{
- /* TODO: fix for several tables in VIEW */
- uint want_privilege= ancestor->table->grant.want_privilege;
- /* real rights will be checked in VIEW field */
- ancestor->table->grant.want_privilege= 0;
- /* aggregate function are allowed */
- thd->allow_sum_func= 1;
- if (!(*item)->fixed && (*item)->fix_fields(thd, ancestor, item))
+ if (!transl->item->fixed &&
+ transl->item->fix_fields(thd, ancestor, &transl->item))
goto err;
- ancestor->table->grant.want_privilege= want_privilege;
+ }
+ for (tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->on_expr && !tbl->on_expr->fixed &&
+ tbl->on_expr->fix_fields(thd, ancestor, &tbl->on_expr))
+ goto err;
+ }
+ if (where && !where->fixed && where->fix_fields(thd, ancestor, &where))
+ goto err;
+ restore_want_privilege();
+
+ /* WHERE/ON resolved => we can rename fields */
+ for (transl= field_translation; transl < end; transl++)
+ {
+ transl->item->rename((char *)transl->name);
}
goto ok;
}
/* view fields translation table */
if (!(transl=
- (Item**)(thd->current_arena->alloc(select->item_list.elements * sizeof(Item*)))))
+ (Field_translator*)(thd->current_arena->
+ alloc(select->item_list.elements *
+ sizeof(Field_translator)))))
{
DBUG_RETURN(1);
}
@@ -1609,22 +1705,29 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds,
used fields correctly.
*/
thd->set_query_id= 1;
+ /* real rights will be checked in VIEW field */
+ save_and_clear_want_privilege();
+ /* aggregate function are allowed */
+ thd->allow_sum_func= 1;
while ((item= it++))
{
- /* TODO: fix for several tables in VIEW */
- uint want_privilege= ancestor->table->grant.want_privilege;
- /* real rights will be checked in VIEW field */
- ancestor->table->grant.want_privilege= 0;
- /* aggregate function are allowed */
- thd->allow_sum_func= 1;
+ /* save original name of view column */
+ char *name= item->name;
if (!item->fixed && item->fix_fields(thd, ancestor, &item))
goto err;
- ancestor->table->grant.want_privilege= want_privilege;
- transl[i++]= item;
+ /* set new item get in fix fields and original column name */
+ transl[i].name= name;
+ transl[i++].item= item;
}
field_translation= transl;
/* TODO: sort this list? Use hash for big number of fields */
+ for (tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->on_expr && !tbl->on_expr->fixed &&
+ tbl->on_expr->fix_fields(thd, ancestor, &tbl->on_expr))
+ goto err;
+ }
if (where ||
(check_opt_type == VIEW_CHECK_CASCADED &&
ancestor->check_option))
@@ -1697,6 +1800,8 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds,
if (arena)
thd->restore_backup_item_arena(arena, &backup);
}
+ restore_want_privilege();
+
/*
fix_fields do not need tables, because new are only AND operation and we
just need recollect statistics
@@ -1705,6 +1810,15 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds,
check_option->fix_fields(thd, 0, &check_option))
goto err;
+ /* WHERE/ON resolved => we can rename fields */
+ {
+ Field_translator *end= field_translation + select->item_list.elements;
+ for (transl= field_translation; transl < end; transl++)
+ {
+ transl->item->rename((char *)transl->name);
+ }
+ }
+
/* full text function moving to current select */
if (view->select_lex.ftfunc_list->elements)
{
@@ -1749,9 +1863,10 @@ void st_table_list::cleanup_items()
if (!field_translation)
return;
- Item **end= field_translation + view->select_lex.item_list.elements;
- for (Item **item= field_translation; item < end; item++)
- (*item)->walk(&Item::cleanup_processor, 0);
+ Field_translator *end= (field_translation +
+ view->select_lex.item_list.elements);
+ for (Field_translator *transl= field_translation; transl < end; transl++)
+ transl->item->walk(&Item::cleanup_processor, 0);
}
@@ -1789,6 +1904,96 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure)
}
+/*
+ Find table in underlying tables by mask and check that only this
+ table belong to given mask
+
+ SYNOPSIS
+ st_table_list::check_single_table()
+ table reference on variable where to store found table
+ (should be 0 on call, to find table, or point to table for
+ unique test)
+ map bit mask of tables
+
+ RETURN
+ FALSE table not found or found only one
+ TRUE found several tables
+*/
+
+bool st_table_list::check_single_table(st_table_list **table, table_map map)
+{
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->table)
+ {
+ if (tbl->table->map & map)
+ {
+ if (*table)
+ return TRUE;
+ else
+ *table= tbl;
+ }
+ }
+ else
+ if (tbl->check_single_table(table, map))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ Set insert_values buffer
+
+ SYNOPSIS
+ set_insert_values()
+ mem_root memory pool for allocating
+
+ RETURN
+ FALSE - OK
+ TRUE - out of memory
+*/
+
+bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
+{
+ if (table)
+ {
+ if (!table->insert_values &&
+ !(table->insert_values= (byte *)alloc_root(mem_root,
+ table->rec_buff_length)))
+ return TRUE;
+ }
+ else
+ {
+ DBUG_ASSERT(view && ancestor && ancestor->next_local);
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ if (tbl->set_insert_values(mem_root))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ clear insert_values reference
+
+ SYNOPSIS
+ clear_insert_values()
+*/
+
+void st_table_list::clear_insert_values()
+{
+ if (table)
+ table->insert_values= 0;
+ else
+ {
+ DBUG_ASSERT(view && ancestor && ancestor->next_local);
+ for (TABLE_LIST *tbl= ancestor; tbl; tbl= tbl->next_local)
+ tbl->clear_insert_values();
+ }
+}
+
+
void Field_iterator_view::set(TABLE_LIST *table)
{
ptr= table->field_translation;
@@ -1810,7 +2015,7 @@ Item *Field_iterator_table::item(THD *thd)
const char *Field_iterator_view::name()
{
- return (*ptr)->name;
+ return ptr->name;
}
diff --git a/sql/table.h b/sql/table.h
index 09c7719980b..ed9c1445cdf 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -160,6 +160,7 @@ struct st_table {
my_bool no_keyread, no_cache;
my_bool clear_query_id; /* To reset query_id for tables and cols */
my_bool auto_increment_field_not_null;
+ my_bool alias_name_used; /* true if table_name is alias */
Field *next_number_field, /* Set if next_number is activated */
*found_next_number_field, /* Set on open */
*rowid_field;
@@ -243,7 +244,7 @@ typedef struct st_schema_table
const char* table_name;
ST_FIELD_INFO *fields_info;
/* Create information_schema table */
- TABLE *(*create_table) (THD *thd, struct st_schema_table *schema_table);
+ TABLE *(*create_table) (THD *thd, struct st_table_list *table_list);
/* Fill table with data */
int (*fill_table) (THD *thd, struct st_table_list *tables, COND *cond);
/* Handle fileds for old SHOW */
@@ -274,6 +275,11 @@ typedef struct st_schema_table
struct st_lex;
class select_union;
+struct Field_translator
+{
+ Item *item;
+ const char *name;
+};
typedef struct st_table_list
{
@@ -308,11 +314,13 @@ typedef struct st_table_list
/* link to select_lex where this table was used */
st_select_lex *select_lex;
st_lex *view; /* link on VIEW lex for merging */
- Item **field_translation; /* array of VIEW fields */
+ Field_translator *field_translation; /* array of VIEW fields */
/* ancestor of this table (VIEW merge algorithm) */
st_table_list *ancestor;
/* most upper view this table belongs to */
st_table_list *belong_to_view;
+ /* list of join table tree leaves */
+ st_table_list *next_leaf;
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
LEX_STRING query; /* text of (CRETE/SELECT) statement */
@@ -332,6 +340,7 @@ typedef struct st_table_list
*/
uint8 effective_with_check;
uint effective_algorithm; /* which algorithm was really used */
+ uint privilege_backup; /* place for saving privileges */
GRANT_INFO grant;
thr_lock_type lock_type;
uint outer_join; /* Which join type */
@@ -366,6 +375,11 @@ typedef struct st_table_list
void cleanup_items();
bool placeholder() {return derived || view; }
void print(THD *thd, String *str);
+ void save_and_clear_want_privilege();
+ void restore_want_privilege();
+ bool check_single_table(st_table_list **table, table_map map);
+ bool set_insert_values(MEM_ROOT *mem_root);
+ void clear_insert_values();
} TABLE_LIST;
class Item;
@@ -400,14 +414,14 @@ public:
class Field_iterator_view: public Field_iterator
{
- Item **ptr, **array_end;
+ Field_translator *ptr, *array_end;
public:
Field_iterator_view() :ptr(0), array_end(0) {}
void set(TABLE_LIST *table);
void next() { ptr++; }
bool end_of_fields() { return ptr == array_end; }
const char *name();
- Item *item(THD *thd) { return *ptr; }
+ Item *item(THD *thd) { return ptr->item; }
Field *field() { return 0; }
};
diff --git a/strings/ctype-uca.c b/strings/ctype-uca.c
index 8df5b3277c1..89c876ad10c 100644
--- a/strings/ctype-uca.c
+++ b/strings/ctype-uca.c
@@ -7288,6 +7288,7 @@ int my_wildcmp_uca(CHARSET_INFO *cs,
{
while (1)
{
+ my_bool escaped= 0;
if ((scan= mb_wc(cs, &w_wc, (const uchar*)wildstr,
(const uchar*)wildend)) <= 0)
return 1;
@@ -7305,6 +7306,7 @@ int my_wildcmp_uca(CHARSET_INFO *cs,
(const uchar*)wildend)) <= 0)
return 1;
wildstr+= scan;
+ escaped= 1;
}
if ((scan= mb_wc(cs, &s_wc, (const uchar*)str,
@@ -7312,7 +7314,7 @@ int my_wildcmp_uca(CHARSET_INFO *cs,
return 1;
str+= scan;
- if (w_wc == (my_wc_t)w_one)
+ if (!escaped && w_wc == (my_wc_t)w_one)
{
result= 1; /* Found an anchor char */
}
diff --git a/strings/ctype-utf8.c b/strings/ctype-utf8.c
index b3097649158..ce9346eb475 100644
--- a/strings/ctype-utf8.c
+++ b/strings/ctype-utf8.c
@@ -1545,31 +1545,33 @@ int my_wildcmp_unicode(CHARSET_INFO *cs,
{
while (1)
{
+ my_bool escaped= 0;
if ((scan= mb_wc(cs, &w_wc, (const uchar*)wildstr,
(const uchar*)wildend)) <= 0)
return 1;
-
- if (w_wc == (my_wc_t)escape)
- {
- wildstr+= scan;
- if ((scan= mb_wc(cs,&w_wc, (const uchar*)wildstr,
- (const uchar*)wildend)) <= 0)
- return 1;
- }
-
+
if (w_wc == (my_wc_t)w_many)
{
result= 1; /* Found an anchor char */
break;
}
-
+
wildstr+= scan;
+ if (w_wc == (my_wc_t)escape)
+ {
+ if ((scan= mb_wc(cs, &w_wc, (const uchar*)wildstr,
+ (const uchar*)wildend)) <= 0)
+ return 1;
+ wildstr+= scan;
+ escaped= 1;
+ }
+
if ((scan= mb_wc(cs, &s_wc, (const uchar*)str,
- (const uchar*)str_end)) <=0)
+ (const uchar*)str_end)) <= 0)
return 1;
str+= scan;
- if (w_wc == (my_wc_t)w_one)
+ if (!escaped && w_wc == (my_wc_t)w_one)
{
result= 1; /* Found an anchor char */
}
diff --git a/strings/uca-dump.c b/strings/uca-dump.c
index db5cb7e999a..dd3b74a55e8 100644
--- a/strings/uca-dump.c
+++ b/strings/uca-dump.c
@@ -269,7 +269,7 @@ int main(int ac, char **av)
*/
int tmp= weight[i];
if (w == 2 && tmp)
- tmp= (int)(0x100 - weight[i]);
+ tmp= (int)(0x20 - weight[i]);
printf("0x%04X", tmp);
@@ -304,7 +304,7 @@ int main(int ac, char **av)
const char *comma= page < MY_UCA_NPAGES-1 ? "," : "";
const char *nline= (page+1) % 4 ? "" : "\n";
if (!pagemaxlen[page])
- printf("NULL %s%s", comma , nline);
+ printf("NULL %s%s%s", w ? " ": "", comma , nline);
else
printf("page%03Xdata%s%s%s", page, pname[w], comma, nline);
}
diff --git a/tests/client_test.c b/tests/client_test.c
index 4f76600aa2e..8bc945eac2c 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -694,7 +694,8 @@ static void verify_prepare_field(MYSQL_RES *result,
as utf8. Field length is calculated as number of characters * maximum
number of bytes a character can occupy.
*/
- DIE_UNLESS(field->length == length * cs->mbmaxlen);
+ if (length)
+ DIE_UNLESS(field->length == length * cs->mbmaxlen);
if (def)
DIE_UNLESS(strcmp(field->def, def) == 0);
}
@@ -7280,7 +7281,7 @@ static void test_explain_bug()
MYSQL_TYPE_STRING, 0, 0, "", 192, 0);
verify_prepare_field(result, 1, "Type", "COLUMN_TYPE",
- MYSQL_TYPE_BLOB, 0, 0, "", 193203, 0);
+ MYSQL_TYPE_BLOB, 0, 0, "", 0, 0);
verify_prepare_field(result, 2, "Null", "IS_NULLABLE",
MYSQL_TYPE_STRING, 0, 0, "", 9, 0);