summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore13
-rw-r--r--libmysqld/Makefile.am5
-rw-r--r--mysql-test/include/mix1.inc108
-rw-r--r--mysql-test/include/ps_conv.inc4
-rw-r--r--mysql-test/r/distinct.result2
-rw-r--r--mysql-test/r/group_by.result12
-rw-r--r--mysql-test/r/innodb_mysql.result135
-rw-r--r--mysql-test/r/join.result58
-rw-r--r--mysql-test/r/ndb_partition_key.result1
-rw-r--r--mysql-test/r/ndb_row_format.result39
-rw-r--r--mysql-test/r/ps_2myisam.result4
-rw-r--r--mysql-test/r/ps_3innodb.result4
-rw-r--r--mysql-test/r/ps_4heap.result4
-rw-r--r--mysql-test/r/ps_5merge.result8
-rw-r--r--mysql-test/r/ps_7ndb.result4
-rw-r--r--mysql-test/r/query_cache.result12
-rw-r--r--mysql-test/r/subselect.result50
-rw-r--r--mysql-test/r/type_datetime.result74
-rw-r--r--mysql-test/r/windows.result7
-rw-r--r--mysql-test/t/disabled.def1
-rw-r--r--mysql-test/t/group_by.test14
-rw-r--r--mysql-test/t/join.test56
-rw-r--r--mysql-test/t/ndb_row_format.test62
-rw-r--r--mysql-test/t/subselect.test56
-rw-r--r--mysql-test/t/type_datetime.test40
-rw-r--r--mysql-test/t/windows.test8
-rw-r--r--sql/Makefile.am15
-rw-r--r--sql/field.cc72
-rw-r--r--sql/field.h46
-rw-r--r--sql/ha_ndbcluster.cc1485
-rw-r--r--sql/ha_ndbcluster.h443
-rw-r--r--sql/ha_ndbcluster_binlog.cc12
-rw-r--r--sql/ha_ndbcluster_cond.cc1426
-rw-r--r--sql/ha_ndbcluster_cond.h475
-rw-r--r--sql/item.cc51
-rw-r--r--sql/item.h47
-rw-r--r--sql/item_cmpfunc.cc400
-rw-r--r--sql/item_cmpfunc.h34
-rw-r--r--sql/item_subselect.cc6
-rw-r--r--sql/item_sum.cc16
-rw-r--r--sql/item_timefunc.cc25
-rw-r--r--sql/key.cc23
-rw-r--r--sql/sp.cc4
-rw-r--r--sql/sql_base.cc5
-rw-r--r--sql/sql_derived.cc4
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_parse.cc2
-rw-r--r--sql/sql_select.cc34
-rw-r--r--sql/sql_show.cc229
-rw-r--r--sql/sql_union.cc19
-rw-r--r--sql/sql_view.cc2
-rw-r--r--sql/sql_yacc.yy2
-rw-r--r--sql/table.cc11
-rw-r--r--sql/table.h6
-rw-r--r--sql/time.cc4
-rw-r--r--storage/innobase/handler/ha_innodb.cc42
-rw-r--r--storage/ndb/include/kernel/signaldata/DictTabInfo.hpp2
-rw-r--r--storage/ndb/include/kernel/signaldata/LqhFrag.hpp3
-rw-r--r--storage/ndb/include/kernel/signaldata/ScanTab.hpp54
-rw-r--r--storage/ndb/include/kernel/signaldata/TupFrag.hpp3
-rw-r--r--storage/ndb/include/ndbapi/NdbDictionary.hpp8
-rw-r--r--storage/ndb/include/ndbapi/NdbOperation.hpp2
-rw-r--r--storage/ndb/include/ndbapi/NdbScanOperation.hpp1
-rw-r--r--storage/ndb/include/ndbapi/NdbTransaction.hpp8
-rw-r--r--storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp2
-rw-r--r--storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp18
-rw-r--r--storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp9
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp37
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp19
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp2
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp130
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp16
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp34
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp2
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp3
-rw-r--r--storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp24
-rw-r--r--storage/ndb/src/kernel/vm/DLFifoList.hpp23
-rw-r--r--storage/ndb/src/mgmapi/mgmapi.cpp8
-rw-r--r--storage/ndb/src/mgmsrv/MgmtSrvr.cpp8
-rw-r--r--storage/ndb/src/ndbapi/NdbBlob.cpp4
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionary.cpp10
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp4
-rw-r--r--storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp1
-rw-r--r--storage/ndb/src/ndbapi/NdbOperationDefine.cpp30
-rw-r--r--storage/ndb/src/ndbapi/NdbScanOperation.cpp68
-rw-r--r--storage/ndb/src/ndbapi/NdbTransaction.cpp11
-rw-r--r--storage/ndb/src/ndbapi/ndberror.c2
-rw-r--r--storage/ndb/test/include/HugoOperations.hpp3
-rw-r--r--storage/ndb/test/include/NdbRestarter.hpp19
-rw-r--r--storage/ndb/test/ndbapi/bank/Bank.hpp2
-rw-r--r--storage/ndb/test/ndbapi/testBasic.cpp72
-rw-r--r--storage/ndb/test/ndbapi/testBlobs.cpp201
-rw-r--r--storage/ndb/test/ndbapi/testNdbApi.cpp73
-rw-r--r--storage/ndb/test/ndbapi/testNodeRestart.cpp69
-rw-r--r--storage/ndb/test/run-test/daily-basic-tests.txt20
-rw-r--r--storage/ndb/test/src/HugoOperations.cpp6
-rw-r--r--storage/ndb/test/src/HugoTransactions.cpp4
-rw-r--r--storage/ndb/test/src/NDBT_Table.cpp1
-rw-r--r--storage/ndb/test/tools/Makefile.am3
-rw-r--r--storage/ndb/test/tools/listen.cpp188
-rw-r--r--storage/ndb/test/tools/rep_latency.cpp304
-rw-r--r--storage/ndb/tools/Makefile.am2
-rw-r--r--storage/ndb/tools/ndb_size.pl1809
-rw-r--r--storage/ndb/tools/ndb_size.tmpl231
-rw-r--r--tests/mysql_client_test.c6
110 files changed, 6426 insertions, 2870 deletions
diff --git a/.bzrignore b/.bzrignore
index 83d4a35c240..70d966f9ccc 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -2641,6 +2641,16 @@ storage/ndb/lib/libNEWTON_BASICTEST_COMMON.so
storage/ndb/lib/libREP_API.so
storage/ndb/lib/libndbclient.so
storage/ndb/lib/libndbclient_extra.so
+storage/ndb/ndbapi-examples/mgmapi_logevent/mgmapi_logevent
+storage/ndb/ndbapi-examples/mgmapi_logevent2/mgmapi_logevent2
+storage/ndb/ndbapi-examples/ndbapi_async/ndbapi_async
+storage/ndb/ndbapi-examples/ndbapi_async1/ndbapi_async1
+storage/ndb/ndbapi-examples/ndbapi_event/ndbapi_event
+storage/ndb/ndbapi-examples/ndbapi_retries/ndbapi_retries
+storage/ndb/ndbapi-examples/ndbapi_scan/ndbapi_scan
+storage/ndb/ndbapi-examples/ndbapi_simple/ndbapi_simple
+storage/ndb/ndbapi-examples/ndbapi_simple_dual/ndbapi_simple_dual
+storage/ndb/ndbapi-examples/ndbapi_simple_index/ndbapi_simple_index
storage/ndb/src/common/debugger/libtrace.dsp
storage/ndb/src/common/debugger/signaldata/libsignaldataprint.dsp
storage/ndb/src/common/logger/liblogger.dsp
@@ -2718,6 +2728,8 @@ storage/ndb/test/ndbapi/testDataBuffers
storage/ndb/test/ndbapi/testDeadlock
storage/ndb/test/ndbapi/testDict
storage/ndb/test/ndbapi/testIndex
+storage/ndb/test/ndbapi/testIndexStat
+storage/ndb/test/ndbapi/testInterpreter
storage/ndb/test/ndbapi/testLcp
storage/ndb/test/ndbapi/testMgm
storage/ndb/test/ndbapi/testNdbApi
@@ -2753,6 +2765,7 @@ storage/ndb/test/tools/hugoScanRead
storage/ndb/test/tools/hugoScanUpdate
storage/ndb/test/tools/listen_event
storage/ndb/test/tools/ndb_cpcc
+storage/ndb/test/tools/rep_latency
storage/ndb/test/tools/restart
storage/ndb/test/tools/verify_index
storage/ndb/tools/ndb_config
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index cef91899e62..a9e951cb9af 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -45,7 +45,7 @@ libmysqlsources = errmsg.c get_password.c libmysql.c client.c pack.c \
noinst_HEADERS = embedded_priv.h emb_qcache.h
sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
- ha_ndbcluster.cc \
+ ha_ndbcluster.cc ha_ndbcluster_cond.cc \
ha_ndbcluster_binlog.cc ha_partition.cc \
handler.cc sql_handler.cc \
hostname.cc init.cc password.c \
@@ -107,6 +107,9 @@ endif
ha_ndbcluster.o:ha_ndbcluster.cc
$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
+ha_ndbcluster_cond.o:ha_ndbcluster_cond.cc
+ $(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
+
ha_ndbcluster_binlog.o: ha_ndbcluster_binlog.cc
$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc
index 34e2ac1048a..352d5a987a2 100644
--- a/mysql-test/include/mix1.inc
+++ b/mysql-test/include/mix1.inc
@@ -168,6 +168,35 @@ connection default;
drop table t1;
disconnect con1;
+#
+# Bug #13191: INSERT...ON DUPLICATE KEY UPDATE of UTF-8 string fields
+# used in partial unique indices.
+#
+
+CREATE TABLE t1(c1 TEXT, UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1(c1 VARCHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
+CREATE TABLE t1(c1 CHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+DROP TABLE t1;
+
--echo End of 4.1 tests
@@ -445,8 +474,87 @@ drop table test;
set global query_cache_type=@save_qcache_type;
set global query_cache_size=@save_qcache_size;
+--source include/innodb_rollback_on_timeout.inc
+
+#
+# Bug #27210: INNODB ON DUPLICATE KEY UPDATE
+#
+
+set @save_qcache_size=@@global.query_cache_size;
+set @save_qcache_type=@@global.query_cache_type;
+set global query_cache_size=10*1024*1024;
+set global query_cache_type=1;
+connect (con1,localhost,root,,);
+connection con1;
+drop table if exists `test`;
+CREATE TABLE `test` (`test1` varchar(3) NOT NULL,
+ `test2` varchar(4) NOT NULL,PRIMARY KEY (`test1`))
+ ENGINE=InnoDB DEFAULT CHARSET=latin1;
+INSERT INTO `test` (`test1`, `test2`) VALUES ('tes', '5678');
+disconnect con1;
+connect (con2,localhost,root,,);
+connection con2;
+select * from test;
+INSERT INTO `test` (`test1`, `test2`) VALUES ('tes', '1234')
+ ON DUPLICATE KEY UPDATE `test2` = '1234';
+select * from test;
+flush tables;
+select * from test;
+disconnect con2;
+connection default;
+drop table test;
+set global query_cache_type=@save_qcache_type;
+set global query_cache_size=@save_qcache_size;
+
--echo End of 5.0 tests
+-- source include/have_innodb.inc
+
+#
+# Bug #27650: INSERT fails after multi-row INSERT of the form:
+# INSERT INTO t (id...) VALUES (NULL...) ON DUPLICATE KEY UPDATE id=VALUES(id)
+#
+
+create table t1(
+id int auto_increment,
+c char(1) not null,
+counter int not null default 1,
+primary key (id),
+unique key (c)
+) engine=innodb;
+
+insert into t1 (id, c) values
+(NULL, 'a'),
+(NULL, 'a')
+on duplicate key update id = values(id), counter = counter + 1;
+
+select * from t1;
+
+insert into t1 (id, c) values
+(NULL, 'b')
+on duplicate key update id = values(id), counter = counter + 1;
+
+select * from t1;
+
+truncate table t1;
+
+insert into t1 (id, c) values (NULL, 'a');
+
+select * from t1;
+
+insert into t1 (id, c) values (NULL, 'b'), (NULL, 'b')
+on duplicate key update id = values(id), c = values(c), counter = counter + 1;
+
+select * from t1;
+
+insert into t1 (id, c) values (NULL, 'a')
+on duplicate key update id = values(id), c = values(c), counter = counter + 1;
+
+select * from t1;
+
+drop table t1;
+
+--echo End of 5.0 tests
#
# Test of behaviour with CREATE ... SELECT
diff --git a/mysql-test/include/ps_conv.inc b/mysql-test/include/ps_conv.inc
index 09290d760ce..195d1061664 100644
--- a/mysql-test/include/ps_conv.inc
+++ b/mysql-test/include/ps_conv.inc
@@ -1171,7 +1171,7 @@ execute stmt1 using @arg00, @arg00, @arg00, @arg00, @arg00 ;
######## SELECT .. WHERE column(date/time/..)=value(DATETIME/LONGBLOB) ########
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -1180,7 +1180,7 @@ select 'true' as found from t9
where c1= 20 and c13= CAST(@arg00 AS DATE) and c14= @arg00 and c15= @arg00 and c16= @arg00
and c17= @arg00 ;
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/distinct.result b/mysql-test/r/distinct.result
index 20538694218..8bf8c95c6fb 100644
--- a/mysql-test/r/distinct.result
+++ b/mysql-test/r/distinct.result
@@ -633,7 +633,7 @@ EXPLAIN SELECT (SELECT DISTINCT ADDDATE(a,1) FROM t1
WHERE ADDDATE(a,1) = '2002-08-03');
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
-2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where; Using temporary
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
CREATE TABLE t2 (a CHAR(5) CHARACTER SET latin1 COLLATE latin1_general_ci);
INSERT INTO t2 VALUES (0xf6);
INSERT INTO t2 VALUES ('oe');
diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result
index c7464bb21d2..7068b62993b 100644
--- a/mysql-test/r/group_by.result
+++ b/mysql-test/r/group_by.result
@@ -1035,6 +1035,18 @@ HAVING SUM(t1_inner.b)+t1_outer.b > 5);
ERROR 42000: 'test.t1_outer.b' isn't in GROUP BY
DROP TABLE t1;
SET SQL_MODE = '';
+SET SQL_MODE = 'ONLY_FULL_GROUP_BY';
+create table t1(f1 int, f2 int);
+select * from t1 group by f1;
+ERROR 42000: 'test.t1.f2' isn't in GROUP BY
+select * from t1 group by f2;
+ERROR 42000: 'test.t1.f1' isn't in GROUP BY
+select * from t1 group by f1, f2;
+f1 f2
+select t1.f1,t.* from t1, t1 t group by 1;
+ERROR 42000: 'test.t.f1' isn't in GROUP BY
+drop table t1;
+SET SQL_MODE = '';
CREATE TABLE t1 (a INT, b INT,
PRIMARY KEY (a),
KEY i2(a,b));
diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result
index fcf70e6b741..e51318af827 100644
--- a/mysql-test/r/innodb_mysql.result
+++ b/mysql-test/r/innodb_mysql.result
@@ -112,6 +112,39 @@ c1
Before and after comparison
0
drop table t1;
+CREATE TABLE t1(c1 TEXT, UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+c1 cnt
+1a 1
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+c1 cnt
+1a 2
+DROP TABLE t1;
+CREATE TABLE t1(c1 VARCHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+c1 cnt
+1a 1
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+c1 cnt
+1a 2
+DROP TABLE t1;
+CREATE TABLE t1(c1 CHAR(2), UNIQUE (c1(1)), cnt INT DEFAULT 1)
+ENGINE=INNODB CHARACTER SET UTF8;
+INSERT INTO t1 (c1) VALUES ('1a');
+SELECT * FROM t1;
+c1 cnt
+1a 1
+INSERT INTO t1 (c1) VALUES ('1b') ON DUPLICATE KEY UPDATE cnt=cnt+1;
+SELECT * FROM t1;
+c1 cnt
+1a 2
+DROP TABLE t1;
End of 4.1 tests
create table t1m (a int) engine = MEMORY;
create table t1i (a int);
@@ -413,6 +446,108 @@ tes 1234
drop table test;
set global query_cache_type=@save_qcache_type;
set global query_cache_size=@save_qcache_size;
+show variables like 'innodb_rollback_on_timeout';
+Variable_name Value
+innodb_rollback_on_timeout OFF
+create table t1 (a int unsigned not null primary key) engine = innodb;
+insert into t1 values (1);
+commit;
+begin work;
+insert into t1 values (2);
+select * from t1;
+a
+1
+2
+begin work;
+insert into t1 values (5);
+select * from t1;
+a
+1
+5
+insert into t1 values (2);
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+select * from t1;
+a
+1
+5
+commit;
+select * from t1;
+a
+1
+2
+commit;
+select * from t1;
+a
+1
+2
+5
+drop table t1;
+set @save_qcache_size=@@global.query_cache_size;
+set @save_qcache_type=@@global.query_cache_type;
+set global query_cache_size=10*1024*1024;
+set global query_cache_type=1;
+drop table if exists `test`;
+Warnings:
+Note 1051 Unknown table 'test'
+CREATE TABLE `test` (`test1` varchar(3) NOT NULL,
+`test2` varchar(4) NOT NULL,PRIMARY KEY (`test1`))
+ENGINE=InnoDB DEFAULT CHARSET=latin1;
+INSERT INTO `test` (`test1`, `test2`) VALUES ('tes', '5678');
+select * from test;
+test1 test2
+tes 5678
+INSERT INTO `test` (`test1`, `test2`) VALUES ('tes', '1234')
+ON DUPLICATE KEY UPDATE `test2` = '1234';
+select * from test;
+test1 test2
+tes 1234
+flush tables;
+select * from test;
+test1 test2
+tes 1234
+drop table test;
+set global query_cache_type=@save_qcache_type;
+set global query_cache_size=@save_qcache_size;
+End of 5.0 tests
+create table t1(
+id int auto_increment,
+c char(1) not null,
+counter int not null default 1,
+primary key (id),
+unique key (c)
+) engine=innodb;
+insert into t1 (id, c) values
+(NULL, 'a'),
+(NULL, 'a')
+on duplicate key update id = values(id), counter = counter + 1;
+select * from t1;
+id c counter
+2 a 2
+insert into t1 (id, c) values
+(NULL, 'b')
+on duplicate key update id = values(id), counter = counter + 1;
+select * from t1;
+id c counter
+2 a 2
+3 b 1
+truncate table t1;
+insert into t1 (id, c) values (NULL, 'a');
+select * from t1;
+id c counter
+1 a 1
+insert into t1 (id, c) values (NULL, 'b'), (NULL, 'b')
+on duplicate key update id = values(id), c = values(c), counter = counter + 1;
+select * from t1;
+id c counter
+1 a 1
+3 b 2
+insert into t1 (id, c) values (NULL, 'a')
+on duplicate key update id = values(id), c = values(c), counter = counter + 1;
+select * from t1;
+id c counter
+3 b 2
+4 a 2
+drop table t1;
End of 5.0 tests
CREATE TABLE t1 (a int, b int);
insert into t1 values (1,1),(1,2);
diff --git a/mysql-test/r/join.result b/mysql-test/r/join.result
index 0a45aa2eb9b..b132c8b2e4c 100644
--- a/mysql-test/r/join.result
+++ b/mysql-test/r/join.result
@@ -764,6 +764,49 @@ natural join t5;
y c b a z
1 3 2 1 4
drop table t1, t2, t3, t4, t5;
+CREATE TABLE t1 (ID INTEGER, Name VARCHAR(50));
+CREATE TABLE t2 (Test_ID INTEGER);
+CREATE VIEW v1 (Test_ID, Description) AS SELECT ID, Name FROM t1;
+CREATE TABLE tv1 SELECT Description AS Name FROM v1 JOIN t2
+USING (Test_ID);
+DESCRIBE tv1;
+Field Type Null Key Default Extra
+Name varchar(50) YES NULL
+CREATE TABLE tv2 SELECT Description AS Name FROM v1 JOIN t2
+ON v1.Test_ID = t2.Test_ID;
+DESCRIBE tv2;
+Field Type Null Key Default Extra
+Name varchar(50) YES NULL
+DROP VIEW v1;
+DROP TABLE t1,t2,tv1,tv2;
+create table t1 (a int, b int);
+insert into t1 values
+(NULL, 1),
+(NULL, 2),
+(NULL, 3),
+(NULL, 4);
+create table t2 (a int not null, primary key(a));
+insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+create table t3 (a int not null, primary key(a));
+insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+flush status;
+select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b;
+a b a a
+explain select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 4
+1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using index
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index
+We expect rnd_next=5, and read_key must be 0 because of short-cutting:
+show status like 'Handler_read%';
+Variable_name Value
+Handler_read_first 0
+Handler_read_key 0
+Handler_read_next 0
+Handler_read_prev 0
+Handler_read_rnd 0
+Handler_read_rnd_next 5
+drop table t1, t2, t3;
create table t1 (a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t2 (a int, b int, filler char(100), key(a), key(b));
@@ -804,19 +847,4 @@ select '^^: The above should be ~= 20 + cost(select * from t1). Value less than
Z
^^: The above should be ~= 20 + cost(select * from t1). Value less than 20 is an error
drop table t1, t2;
-CREATE TABLE t1 (ID INTEGER, Name VARCHAR(50));
-CREATE TABLE t2 (Test_ID INTEGER);
-CREATE VIEW v1 (Test_ID, Description) AS SELECT ID, Name FROM t1;
-CREATE TABLE tv1 SELECT Description AS Name FROM v1 JOIN t2
-USING (Test_ID);
-DESCRIBE tv1;
-Field Type Null Key Default Extra
-Name varchar(50) YES NULL
-CREATE TABLE tv2 SELECT Description AS Name FROM v1 JOIN t2
-ON v1.Test_ID = t2.Test_ID;
-DESCRIBE tv2;
-Field Type Null Key Default Extra
-Name varchar(50) YES NULL
-DROP VIEW v1;
-DROP TABLE t1,t2,tv1,tv2;
End of 5.0 tests.
diff --git a/mysql-test/r/ndb_partition_key.result b/mysql-test/r/ndb_partition_key.result
index 685137b7710..60e96ce03b6 100644
--- a/mysql-test/r/ndb_partition_key.result
+++ b/mysql-test/r/ndb_partition_key.result
@@ -58,6 +58,7 @@ Length of frm data: #
Row Checksum: 1
Row GCI: 1
SingleUserMode: 0
+ForceVarPart: 1
TableStatus: Retrieved
-- Attributes --
a Int PRIMARY KEY AT=FIXED ST=MEMORY
diff --git a/mysql-test/r/ndb_row_format.result b/mysql-test/r/ndb_row_format.result
new file mode 100644
index 00000000000..6db289c75aa
--- /dev/null
+++ b/mysql-test/r/ndb_row_format.result
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7;
+drop database if exists mysqltest;
+CREATE TABLE t1
+( a INT KEY,
+b VARCHAR(10) )
+ROW_FORMAT=FIXED
+ENGINE=NDB;
+ERROR HY000: Can't create table 'test.t1' (errno: 138)
+SHOW WARNINGS;
+Level Code Message
+Error 1466 Table storage engine 'ndbcluster' does not support the create option 'Row format FIXED incompatible with variable sized attribute'
+Error 1005 Can't create table 'test.t1' (errno: 138)
+CREATE TABLE t1
+( a INT KEY,
+b INT )
+ENGINE=NDB;
+ForceVarPart: 1
+DROP TABLE t1;
+CREATE TABLE t1
+( a INT KEY,
+b INT )
+ROW_FORMAT=DEFAULT
+ENGINE=NDB;
+ForceVarPart: 1
+DROP TABLE t1;
+CREATE TABLE t1
+( a INT KEY,
+b INT )
+ROW_FORMAT=FIXED
+ENGINE=NDB;
+ForceVarPart: 0
+DROP TABLE t1;
+CREATE TABLE t1
+( a INT KEY,
+b INT )
+ROW_FORMAT=DYNAMIC
+ENGINE=NDB;
+ForceVarPart: 1
+DROP TABLE t1;
diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result
index d18bb8dc434..2ce35dae092 100644
--- a/mysql-test/r/ps_2myisam.result
+++ b/mysql-test/r/ps_2myisam.result
@@ -3077,7 +3077,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -3090,7 +3090,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result
index e6ff668760c..70181ecccdc 100644
--- a/mysql-test/r/ps_3innodb.result
+++ b/mysql-test/r/ps_3innodb.result
@@ -3060,7 +3060,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -3073,7 +3073,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result
index 046992806cc..19be5a2707e 100644
--- a/mysql-test/r/ps_4heap.result
+++ b/mysql-test/r/ps_4heap.result
@@ -3061,7 +3061,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -3074,7 +3074,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result
index bbdb80df5d4..ebdc5c8c9fd 100644
--- a/mysql-test/r/ps_5merge.result
+++ b/mysql-test/r/ps_5merge.result
@@ -2997,7 +2997,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -3010,7 +3010,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -6018,7 +6018,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -6031,7 +6031,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/ps_7ndb.result b/mysql-test/r/ps_7ndb.result
index 20d38ba84bd..2cffb698fc0 100644
--- a/mysql-test/r/ps_7ndb.result
+++ b/mysql-test/r/ps_7ndb.result
@@ -3060,7 +3060,7 @@ found
true
set @arg00= CAST('1991-01-01 01:01:01' as datetime) ;
select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
@@ -3073,7 +3073,7 @@ and c17= @arg00 ;
found
true
prepare stmt1 from "select 'true' as found from t9
-where c1= 20 and c13= CAST('1991-01-01 01:01:01' as datetime) and
+where c1= 20 and c13= CAST('1991-01-01 00:00:00' as datetime) and
c14= CAST('1991-01-01 01:01:01' as datetime) and
c15= CAST('1991-01-01 01:01:01' as datetime) and
c16= CAST('1991-01-01 01:01:01' as datetime) and
diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index 866701d2204..66283f16120 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -947,25 +947,19 @@ COUNT(*)
0
Warnings:
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
-Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
-Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
-Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
+Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 0
SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050328 invalid';
COUNT(*)
0
Warnings:
Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1
-Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 1
-Warning 1292 Truncated incorrect INTEGER value: '20050328 invalid'
-Warning 1292 Truncated incorrect INTEGER value: '20050328 invalid'
+Warning 1292 Incorrect datetime value: '20050328 invalid' for column 'date' at row 0
SELECT COUNT(*) FROM t1 WHERE date BETWEEN '20050326' AND '20050327 invalid';
COUNT(*)
0
Warnings:
Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
-Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 1
-Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
-Warning 1292 Truncated incorrect INTEGER value: '20050327 invalid'
+Warning 1292 Incorrect datetime value: '20050327 invalid' for column 'date' at row 0
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index 8263350fe58..0143b0c7fd3 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -394,13 +394,13 @@ EXPLAIN EXTENDED SELECT DISTINCT date FROM t1 WHERE date='2002-08-03';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 index NULL PRIMARY 43 NULL 2 100.00 Using where; Using index
Warnings:
-Note 1003 select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = 20020803)
+Note 1003 select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = _latin1'2002-08-03')
EXPLAIN EXTENDED SELECT (SELECT DISTINCT date FROM t1 WHERE date='2002-08-03');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
2 SUBQUERY t1 index NULL PRIMARY 43 NULL 2 100.00 Using where; Using index
Warnings:
-Note 1003 select (select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = 20020803)) AS `(SELECT DISTINCT date FROM t1 WHERE date='2002-08-03')`
+Note 1003 select (select distinct `test`.`t1`.`date` AS `date` from `test`.`t1` where (`test`.`t1`.`date` = _latin1'2002-08-03')) AS `(SELECT DISTINCT date FROM t1 WHERE date='2002-08-03')`
SELECT DISTINCT date FROM t1 WHERE date='2002-08-03';
date
2002-08-03
@@ -4012,6 +4012,52 @@ WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a;
ERROR HY000: Invalid use of group function
SET @@sql_mode=default;
DROP TABLE t1;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 values (1),(1),(1),(1);
+CREATE TABLE t2 (x INT);
+INSERT INTO t1 values (1000),(1001),(1002);
+SELECT SUM( (SELECT COUNT(a) FROM t2) ) FROM t1;
+ERROR HY000: Invalid use of group function
+SELECT SUM( (SELECT SUM(COUNT(a)) FROM t2) ) FROM t1;
+ERROR HY000: Invalid use of group function
+SELECT COUNT(1) FROM DUAL;
+COUNT(1)
+1
+SELECT SUM( (SELECT AVG( (SELECT t1.a FROM t2) ) FROM DUAL) ) FROM t1;
+ERROR HY000: Invalid use of group function
+SELECT
+SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING t1.a < 12) ) FROM t2) )
+FROM t1;
+ERROR HY000: Invalid use of group function
+SELECT t1.a as XXA,
+SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING XXA < 12) ) FROM t2) )
+FROM t1;
+ERROR HY000: Invalid use of group function
+DROP TABLE t1,t2;
+End of 5.0 tests.
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (2,22),(1,11),(2,22);
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a;
+a
+1
+2
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a;
+a
+SELECT a FROM t1 t0
+WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a;
+a
+1
+2
+SET @@sql_mode='ansi';
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a;
+ERROR HY000: Invalid use of group function
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a;
+ERROR HY000: Invalid use of group function
+SELECT a FROM t1 t0
+WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a;
+ERROR HY000: Invalid use of group function
+SET @@sql_mode=default;
+DROP TABLE t1;
CREATE TABLE t1 (s1 char(1));
INSERT INTO t1 VALUES ('a');
SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1);
diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result
index 045a6cd1c0a..95bd3061fa6 100644
--- a/mysql-test/r/type_datetime.result
+++ b/mysql-test/r/type_datetime.result
@@ -192,6 +192,80 @@ CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS DECIMA
SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6));
CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6))
101112.098700
+create table t1 (f1 date, f2 datetime, f3 timestamp);
+insert into t1(f1) values(curdate());
+select curdate() < now(), f1 < now(), cast(f1 as date) < now() from t1;
+curdate() < now() f1 < now() cast(f1 as date) < now()
+1 1 1
+delete from t1;
+insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01');
+insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01');
+insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01');
+insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00');
+insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01');
+select f1, f3 from t1 where f1 >= '2001-02-05 00:00:00' and f3 <= '2001-04-15';
+f1 f3
+2001-02-05 2001-02-05 01:01:01
+2001-03-10 2001-03-10 01:01:01
+2001-04-15 2001-04-15 00:00:00
+select f1, f3 from t1 where f1 >= '2001-2-5 0:0:0' and f2 <= '2001-4-15';
+f1 f3
+2001-02-05 2001-02-05 01:01:01
+2001-03-10 2001-03-10 01:01:01
+2001-04-15 2001-04-15 00:00:00
+select f1, f2 from t1 where if(1, f1, 0) >= f2;
+f1 f2
+2001-02-05 2001-02-05 00:00:00
+2001-03-10 2001-03-09 01:01:01
+2001-04-15 2001-04-15 00:00:00
+select 1 from dual where cast('2001-1-1 2:3:4' as date) = cast('2001-01-01' as datetime);
+1
+1
+select f1, f2, UNIX_TIMESTAMP(f2), UNIX_TIMESTAMP(f1),
+f1 > f2, f1 = f2, f1 < f2
+from t1;
+f1 f2 UNIX_TIMESTAMP(f2) UNIX_TIMESTAMP(f1) f1 > f2 f1 = f2 f1 < f2
+2001-01-01 2001-01-01 01:01:01 978300061 978296400 0 0 1
+2001-02-05 2001-02-05 00:00:00 981320400 981320400 0 1 0
+2001-03-10 2001-03-09 01:01:01 984088861 984171600 1 0 0
+2001-04-15 2001-04-15 00:00:00 987282000 987282000 0 1 0
+2001-05-20 2001-05-20 01:01:01 990309661 990306000 0 0 1
+drop table t1;
+create table t1 (f1 date, f2 datetime, f3 timestamp);
+insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01');
+insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01');
+insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01');
+insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00');
+insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01');
+select f2 from t1 where f2 between '2001-2-5' and '01-04-14';
+f2
+2001-02-05 00:00:00
+2001-03-09 01:01:01
+select f1, f2, f3 from t1 where f1 between f2 and f3;
+f1 f2 f3
+2001-02-05 2001-02-05 00:00:00 2001-02-05 01:01:01
+2001-03-10 2001-03-09 01:01:01 2001-03-10 01:01:01
+2001-04-15 2001-04-15 00:00:00 2001-04-15 00:00:00
+select f1, f2, f3 from t1 where cast(f1 as datetime) between f2 and
+cast(f3 as date);
+f1 f2 f3
+2001-02-05 2001-02-05 00:00:00 2001-02-05 01:01:01
+2001-03-10 2001-03-09 01:01:01 2001-03-10 01:01:01
+2001-04-15 2001-04-15 00:00:00 2001-04-15 00:00:00
+select f2 from t1 where '2001-04-10 12:34:56' between f2 and '01-05-01';
+f2
+2001-01-01 01:01:01
+2001-02-05 00:00:00
+2001-03-09 01:01:01
+select f2, f3 from t1 where '01-03-10' between f2 and f3;
+f2 f3
+2001-03-09 01:01:01 2001-03-10 01:01:01
+select f2 from t1 where DATE(f2) between "2001-4-15" AND "01-4-15";
+f2
+2001-04-15 00:00:00
+SELECT 1 from dual where NOW() BETWEEN CURRENT_DATE() - INTERVAL 1 DAY AND CURRENT_DATE();
+1
+drop table t1;
set @org_mode=@@sql_mode;
create table t1 (da date default '1962-03-03 23:33:34', dt datetime default '1962-03-03');
Warnings:
diff --git a/mysql-test/r/windows.result b/mysql-test/r/windows.result
index 7472b724f47..c97d3dd8867 100644
--- a/mysql-test/r/windows.result
+++ b/mysql-test/r/windows.result
@@ -12,6 +12,13 @@ Warnings:
Warning 0 DATA DIRECTORY option ignored
Warning 0 INDEX DIRECTORY option ignored
drop table t1;
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (1,1);
+EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2));
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL No tables used
+End of 5.0 tests.
create procedure proc_1() install plugin my_plug soname '\\root\\some_plugin.dll';
call proc_1();
ERROR HY000: No paths allowed for shared library
diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def
index ad2c7a6c08c..d1f8db97131 100644
--- a/mysql-test/t/disabled.def
+++ b/mysql-test/t/disabled.def
@@ -28,7 +28,6 @@ rpl_ddl : BUG#26418 2007-03-01 mleich Slave out of sync after C
rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement
rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement
rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly
-rpl_udf : BUG#27564 2007-03-31 lars New test case for rpl of UDF shows valgrind failure
synchronization : Bug#24529 Test 'synchronization' fails on Mac pushbuild; Also on Linux 64 bit.
# the below testcase have been reworked to avoid the bug, test contains comment, keep bug open
diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test
index fb9c09d4763..4e21568377f 100644
--- a/mysql-test/t/group_by.test
+++ b/mysql-test/t/group_by.test
@@ -752,6 +752,20 @@ SELECT b FROM t1 AS t1_outer GROUP BY a HAVING t1_outer.a IN
HAVING SUM(t1_inner.b)+t1_outer.b > 5);
DROP TABLE t1;
SET SQL_MODE = '';
+#
+# Bug#27874: Non-grouped columns are allowed by * in ONLY_FULL_GROUP_BY mode.
+#
+SET SQL_MODE = 'ONLY_FULL_GROUP_BY';
+create table t1(f1 int, f2 int);
+--error 1055
+select * from t1 group by f1;
+--error 1055
+select * from t1 group by f2;
+select * from t1 group by f1, f2;
+--error 1055
+select t1.f1,t.* from t1, t1 t group by 1;
+drop table t1;
+SET SQL_MODE = '';
#
# Bug #21174: Index degrades sort performance and
diff --git a/mysql-test/t/join.test b/mysql-test/t/join.test
index 72d78dd7074..6c9270f76be 100644
--- a/mysql-test/t/join.test
+++ b/mysql-test/t/join.test
@@ -593,6 +593,46 @@ drop table t1, t2, t3, t4, t5;
# End of tests for WL#2486 - natural/using join
#
+# BUG#25106: A USING clause in combination with a VIEW results in column
+# aliases ignored
+#
+CREATE TABLE t1 (ID INTEGER, Name VARCHAR(50));
+CREATE TABLE t2 (Test_ID INTEGER);
+CREATE VIEW v1 (Test_ID, Description) AS SELECT ID, Name FROM t1;
+
+CREATE TABLE tv1 SELECT Description AS Name FROM v1 JOIN t2
+ USING (Test_ID);
+DESCRIBE tv1;
+CREATE TABLE tv2 SELECT Description AS Name FROM v1 JOIN t2
+ ON v1.Test_ID = t2.Test_ID;
+DESCRIBE tv2;
+
+DROP VIEW v1;
+DROP TABLE t1,t2,tv1,tv2;
+
+
+# BUG#27939: Early NULLs filtering doesn't work for eq_ref access
+create table t1 (a int, b int);
+insert into t1 values
+ (NULL, 1),
+ (NULL, 2),
+ (NULL, 3),
+ (NULL, 4);
+
+create table t2 (a int not null, primary key(a));
+insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+create table t3 (a int not null, primary key(a));
+insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
+
+flush status;
+select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b;
+explain select * from t1, t2, t3 where t3.a=t1.a and t2.a=t1.b;
+--echo We expect rnd_next=5, and read_key must be 0 because of short-cutting:
+show status like 'Handler_read%';
+drop table t1, t2, t3;
+
+#
# BUG#14940: Make E(#rows) from "range" access be re-used by range optimizer
#
create table t1 (a int);
@@ -631,21 +671,5 @@ select '^^: The above should be ~= 20 + cost(select * from t1). Value less than
drop table t1, t2;
-# BUG#25106: A USING clause in combination with a VIEW results in column
-# aliases ignored
-#
-CREATE TABLE t1 (ID INTEGER, Name VARCHAR(50));
-CREATE TABLE t2 (Test_ID INTEGER);
-CREATE VIEW v1 (Test_ID, Description) AS SELECT ID, Name FROM t1;
-
-CREATE TABLE tv1 SELECT Description AS Name FROM v1 JOIN t2
- USING (Test_ID);
-DESCRIBE tv1;
-CREATE TABLE tv2 SELECT Description AS Name FROM v1 JOIN t2
- ON v1.Test_ID = t2.Test_ID;
-DESCRIBE tv2;
-
-DROP VIEW v1;
-DROP TABLE t1,t2,tv1,tv2;
--echo End of 5.0 tests.
diff --git a/mysql-test/t/ndb_row_format.test b/mysql-test/t/ndb_row_format.test
new file mode 100644
index 00000000000..9668d8ea515
--- /dev/null
+++ b/mysql-test/t/ndb_row_format.test
@@ -0,0 +1,62 @@
+-- source include/have_ndb.inc
+-- source include/not_embedded.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7;
+drop database if exists mysqltest;
+--enable_warnings
+
+#
+# some negative tests
+#
+
+# cannot have ROW_FORMAT=FIXED and var attrs mixed
+--error 1005
+CREATE TABLE t1
+ ( a INT KEY,
+ b VARCHAR(10) )
+ ROW_FORMAT=FIXED
+ ENGINE=NDB;
+
+# warnings give more detail on the error
+SHOW WARNINGS;
+
+
+#
+# Check force var part for different ROW_FORMAT
+#
+
+# default => ForceVarPart: 1
+CREATE TABLE t1
+ ( a INT KEY,
+ b INT )
+ ENGINE=NDB;
+--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -d test t1 | grep ForceVarPart
+DROP TABLE t1;
+
+# explicit DEFAULT => ForceVarPart: 1
+CREATE TABLE t1
+ ( a INT KEY,
+ b INT )
+ ROW_FORMAT=DEFAULT
+ ENGINE=NDB;
+--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -d test t1 | grep ForceVarPart
+DROP TABLE t1;
+
+# FIXED => ForceVarPart: 0
+CREATE TABLE t1
+ ( a INT KEY,
+ b INT )
+ ROW_FORMAT=FIXED
+ ENGINE=NDB;
+--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -d test t1 | grep ForceVarPart
+DROP TABLE t1;
+
+# DYNAMIC => ForceVarPart: 1
+CREATE TABLE t1
+ ( a INT KEY,
+ b INT )
+ ROW_FORMAT=DYNAMIC
+ ENGINE=NDB;
+--exec $NDB_TOOLS_DIR/ndb_desc --no-defaults -d test t1 | grep ForceVarPart
+DROP TABLE t1;
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index 3b7cc2abb73..0648c3ebda0 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -2819,6 +2819,62 @@ SELECT tt.a, MAX(
FROM t1 as tt GROUP BY tt.a;
DROP TABLE t1;
+#
+# Bug #27348: SET FUNCTION used in a subquery from WHERE condition
+#
+
+CREATE TABLE t1 (a int, b int);
+INSERT INTO t1 VALUES (2,22),(1,11),(2,22);
+
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a;
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a;
+
+SELECT a FROM t1 t0
+ WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a;
+
+SET @@sql_mode='ansi';
+--error 1111
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a;
+--error 1111
+SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a;
+
+--error 1111
+SELECT a FROM t1 t0
+ WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a;
+
+SET @@sql_mode=default;
+
+DROP TABLE t1;
+
+#
+# Bug #27363: nested aggregates in outer, subquery / sum(select
+# count(outer))
+#
+CREATE TABLE t1 (a INT); INSERT INTO t1 values (1),(1),(1),(1);
+CREATE TABLE t2 (x INT); INSERT INTO t1 values (1000),(1001),(1002);
+
+--error ER_INVALID_GROUP_FUNC_USE
+SELECT SUM( (SELECT COUNT(a) FROM t2) ) FROM t1;
+--error ER_INVALID_GROUP_FUNC_USE
+SELECT SUM( (SELECT SUM(COUNT(a)) FROM t2) ) FROM t1;
+SELECT COUNT(1) FROM DUAL;
+
+--error ER_INVALID_GROUP_FUNC_USE
+SELECT SUM( (SELECT AVG( (SELECT t1.a FROM t2) ) FROM DUAL) ) FROM t1;
+
+--error ER_INVALID_GROUP_FUNC_USE
+SELECT
+ SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING t1.a < 12) ) FROM t2) )
+FROM t1;
+
+--error ER_INVALID_GROUP_FUNC_USE
+SELECT t1.a as XXA,
+ SUM( (SELECT AVG( (SELECT COUNT(*) FROM t1 t HAVING XXA < 12) ) FROM t2) )
+FROM t1;
+
+DROP TABLE t1,t2;
+
+--echo End of 5.0 tests.
#
# Bug #27348: SET FUNCTION used in a subquery from WHERE condition
diff --git a/mysql-test/t/type_datetime.test b/mysql-test/t/type_datetime.test
index b4c10408b37..eeed14acf15 100644
--- a/mysql-test/t/type_datetime.test
+++ b/mysql-test/t/type_datetime.test
@@ -142,6 +142,46 @@ SELECT CAST(CAST('2006-08-10 10:11:12' AS DATETIME) + INTERVAL 14 MICROSECOND AS
SELECT CAST(CAST('10:11:12.098700' AS TIME) AS DECIMAL(20,6));
#
+# Bug#27590: Wrong DATE/DATETIME comparison.
+#
+create table t1 (f1 date, f2 datetime, f3 timestamp);
+insert into t1(f1) values(curdate());
+select curdate() < now(), f1 < now(), cast(f1 as date) < now() from t1;
+delete from t1;
+insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01');
+insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01');
+insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01');
+insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00');
+insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01');
+select f1, f3 from t1 where f1 >= '2001-02-05 00:00:00' and f3 <= '2001-04-15';
+select f1, f3 from t1 where f1 >= '2001-2-5 0:0:0' and f2 <= '2001-4-15';
+select f1, f2 from t1 where if(1, f1, 0) >= f2;
+select 1 from dual where cast('2001-1-1 2:3:4' as date) = cast('2001-01-01' as datetime);
+select f1, f2, UNIX_TIMESTAMP(f2), UNIX_TIMESTAMP(f1),
+ f1 > f2, f1 = f2, f1 < f2
+ from t1;
+drop table t1;
+
+#
+# Bug#16377: Wrong DATE/DATETIME comparison in BETWEEN function.
+#
+create table t1 (f1 date, f2 datetime, f3 timestamp);
+insert into t1 values('2001-01-01','2001-01-01 01:01:01','2001-01-01 01:01:01');
+insert into t1 values('2001-02-05','2001-02-05 00:00:00','2001-02-05 01:01:01');
+insert into t1 values('2001-03-10','2001-03-09 01:01:01','2001-03-10 01:01:01');
+insert into t1 values('2001-04-15','2001-04-15 00:00:00','2001-04-15 00:00:00');
+insert into t1 values('2001-05-20','2001-05-20 01:01:01','2001-05-20 01:01:01');
+select f2 from t1 where f2 between '2001-2-5' and '01-04-14';
+select f1, f2, f3 from t1 where f1 between f2 and f3;
+select f1, f2, f3 from t1 where cast(f1 as datetime) between f2 and
+ cast(f3 as date);
+select f2 from t1 where '2001-04-10 12:34:56' between f2 and '01-05-01';
+select f2, f3 from t1 where '01-03-10' between f2 and f3;
+select f2 from t1 where DATE(f2) between "2001-4-15" AND "01-4-15";
+SELECT 1 from dual where NOW() BETWEEN CURRENT_DATE() - INTERVAL 1 DAY AND CURRENT_DATE();
+drop table t1;
+
+#
# Test of storing datetime into date fields
#
diff --git a/mysql-test/t/windows.test b/mysql-test/t/windows.test
index a10d54b5452..0be81a95b9a 100644
--- a/mysql-test/t/windows.test
+++ b/mysql-test/t/windows.test
@@ -29,6 +29,14 @@ drop table t1;
# End of 4.1 tests
#
+# Bug #27811: The variable 'join_tab' is being used without being defined
+#
+CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (1,1);
+EXPLAIN SELECT * FROM t1 WHERE b = (SELECT max(2));
+
+--echo End of 5.0 tests.
+
+#
# Bug #20665: All commands supported in Stored Procedures should work in
# Prepared Statements
#
diff --git a/sql/Makefile.am b/sql/Makefile.am
index a04386611f8..465e5c843f4 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -48,9 +48,9 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
procedure.h sql_class.h sql_lex.h sql_list.h \
sql_map.h sql_string.h unireg.h \
sql_error.h field.h handler.h mysqld_suffix.h \
- ha_partition.h \
- ha_ndbcluster.h ha_ndbcluster_binlog.h \
- ha_ndbcluster_tables.h rpl_constants.h \
+ ha_ndbcluster.h ha_ndbcluster_cond.h \
+ ha_ndbcluster_binlog.h ha_ndbcluster_tables.h \
+ ha_partition.h rpl_constants.h \
opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
log.h sql_show.h rpl_rli.h rpl_mi.h \
sql_select.h structs.h table.h sql_udf.h hash_filo.h \
@@ -92,8 +92,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
log_event_old.cc rpl_record_old.cc \
discover.cc time.cc opt_range.cc opt_sum.cc \
records.cc filesort.cc handler.cc \
- ha_partition.cc \
- ha_ndbcluster.cc ha_ndbcluster_binlog.cc \
+ ha_ndbcluster.cc ha_ndbcluster_cond.cc \
+ ha_ndbcluster_binlog.cc ha_partition.cc \
sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
@@ -156,10 +156,13 @@ lex_hash.h: gen_lex_hash.cc lex.h
./gen_lex_hash$(EXEEXT) > $@-t
$(MV) $@-t $@
-# the following three should eventually be moved out of this directory
+# the following four should eventually be moved out of this directory
ha_ndbcluster.o:ha_ndbcluster.cc ha_ndbcluster.h
$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
+ha_ndbcluster_cond.o:ha_ndbcluster_cond.cc ha_ndbcluster_cond.h
+ $(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
+
ha_ndbcluster_binlog.o:ha_ndbcluster_binlog.cc ha_ndbcluster_binlog.h
$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
diff --git a/sql/field.cc b/sql/field.cc
index 82f8283ba56..e8718c9e407 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -6099,13 +6099,21 @@ uint Field::is_equal(create_field *new_field)
}
+/* If one of the fields is binary and the other one isn't return 1 else 0 */
+
+bool Field_str::compare_str_field_flags(create_field *new_field, uint32 flags)
+{
+ return (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
+ !(flags & (BINCMP_FLAG | BINARY_FLAG))) ||
+ (!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
+ (flags & (BINCMP_FLAG | BINARY_FLAG))));
+}
+
+
uint Field_str::is_equal(create_field *new_field)
{
- if (((new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
- !(flags & (BINCMP_FLAG | BINARY_FLAG))) ||
- (!(new_field->flags & (BINCMP_FLAG | BINARY_FLAG)) &&
- (flags & (BINCMP_FLAG | BINARY_FLAG))))
- return 0; /* One of the fields is binary and the other one isn't */
+ if (compare_str_field_flags(new_field, flags))
+ return 0;
return ((new_field->sql_type == real_type()) &&
new_field->charset == field_charset &&
@@ -6395,6 +6403,15 @@ uint Field_string::max_packed_col_length(uint max_length)
return (max_length > 255 ? 2 : 1)+max_length;
}
+uint Field_string::get_key_image(char *buff, uint length, imagetype type_arg)
+{
+ uint bytes = my_charpos(field_charset, ptr, ptr + field_length,
+ length / field_charset->mbmaxlen);
+ memcpy(buff, ptr, bytes);
+ if (bytes < length)
+ bzero(buff + bytes, length - bytes);
+ return bytes;
+}
Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
bool keep_type)
@@ -6853,9 +6870,7 @@ uint Field_varstring::max_packed_col_length(uint max_length)
return (max_length > 255 ? 2 : 1)+max_length;
}
-
-void Field_varstring::get_key_image(char *buff, uint length,
- imagetype type_arg)
+uint Field_varstring::get_key_image(char *buff, uint length, imagetype type)
{
uint f_length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr);
uint local_char_length= length / field_charset->mbmaxlen;
@@ -6874,6 +6889,7 @@ void Field_varstring::get_key_image(char *buff, uint length,
*/
bzero(buff+HA_KEY_BLOB_LENGTH+f_length, (length-f_length));
}
+ return HA_KEY_BLOB_LENGTH+f_length;
}
@@ -7278,7 +7294,7 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr,
/* The following is used only when comparing a key */
-void Field_blob::get_key_image(char *buff, uint length, imagetype type_arg)
+uint Field_blob::get_key_image(char *buff,uint length, imagetype type_arg)
{
uint32 blob_length= get_length(ptr);
char *blob;
@@ -7290,16 +7306,17 @@ void Field_blob::get_key_image(char *buff, uint length, imagetype type_arg)
MBR mbr;
Geometry_buffer buffer;
Geometry *gobj;
+ const uint image_length= SIZEOF_STORED_DOUBLE*4;
if (blob_length < SRID_SIZE)
{
- bzero(buff, SIZEOF_STORED_DOUBLE*4);
- return;
+ bzero(buff, image_length);
+ return image_length;
}
get_ptr(&blob);
gobj= Geometry::construct(&buffer, blob, blob_length);
if (!gobj || gobj->get_mbr(&mbr, &dummy))
- bzero(buff, SIZEOF_STORED_DOUBLE*4);
+ bzero(buff, image_length);
else
{
float8store(buff, mbr.xmin);
@@ -7307,7 +7324,7 @@ void Field_blob::get_key_image(char *buff, uint length, imagetype type_arg)
float8store(buff+16, mbr.ymin);
float8store(buff+24, mbr.ymax);
}
- return;
+ return image_length;
}
#endif /*HAVE_SPATIAL*/
@@ -7328,6 +7345,7 @@ void Field_blob::get_key_image(char *buff, uint length, imagetype type_arg)
}
int2store(buff,length);
memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length);
+ return HA_KEY_BLOB_LENGTH+length;
}
@@ -7611,9 +7629,21 @@ uint Field_blob::max_packed_col_length(uint max_length)
}
+uint Field_blob::is_equal(create_field *new_field)
+{
+ if (compare_str_field_flags(new_field, flags))
+ return 0;
+
+ return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
+ && new_field->charset == field_charset &&
+ ((Field_blob *)new_field->field)->max_data_length() ==
+ max_data_length());
+}
+
+
#ifdef HAVE_SPATIAL
-void Field_geom::get_key_image(char *buff, uint length, imagetype type_arg)
+uint Field_geom::get_key_image(char *buff, uint length, imagetype type)
{
char *blob;
const char *dummy;
@@ -7621,16 +7651,17 @@ void Field_geom::get_key_image(char *buff, uint length, imagetype type_arg)
ulong blob_length= get_length(ptr);
Geometry_buffer buffer;
Geometry *gobj;
+ const uint image_length= SIZEOF_STORED_DOUBLE*4;
if (blob_length < SRID_SIZE)
{
- bzero(buff, SIZEOF_STORED_DOUBLE*4);
- return;
+ bzero(buff, image_length);
+ return image_length;
}
get_ptr(&blob);
gobj= Geometry::construct(&buffer, blob, blob_length);
if (!gobj || gobj->get_mbr(&mbr, &dummy))
- bzero(buff, SIZEOF_STORED_DOUBLE*4);
+ bzero(buff, image_length);
else
{
float8store(buff, mbr.xmin);
@@ -7638,6 +7669,7 @@ void Field_geom::get_key_image(char *buff, uint length, imagetype type_arg)
float8store(buff + 16, mbr.ymin);
float8store(buff + 24, mbr.ymax);
}
+ return image_length;
}
@@ -8421,7 +8453,7 @@ int Field_bit::cmp_offset(uint row_offset)
}
-void Field_bit::get_key_image(char *buff, uint length, imagetype type_arg)
+uint Field_bit::get_key_image(char *buff, uint length, imagetype type_arg)
{
if (bit_len)
{
@@ -8429,7 +8461,9 @@ void Field_bit::get_key_image(char *buff, uint length, imagetype type_arg)
*buff++= bits;
length--;
}
- memcpy(buff, ptr, min(length, bytes_in_rec));
+ uint data_length = min(length, bytes_in_rec);
+ memcpy(buff, ptr, data_length);
+ return data_length + 1;
}
diff --git a/sql/field.h b/sql/field.h
index 441ff9079c1..f9d77fe17ab 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -276,8 +276,39 @@ public:
{ memcpy(buff,ptr,length); }
inline void set_image(char *buff,uint length, CHARSET_INFO *cs)
{ memcpy(ptr,buff,length); }
- virtual void get_key_image(char *buff, uint length, imagetype type_arg)
- { get_image(buff,length, &my_charset_bin); }
+
+
+ /*
+ Copy a field part into an output buffer.
+
+ SYNOPSIS
+ Field::get_key_image()
+ buff [out] output buffer
+ length output buffer size
+ type itMBR for geometry blobs, otherwise itRAW
+
+ DESCRIPTION
+ This function makes a copy of field part of size equal to or
+ less than "length" parameter value.
+ For fields of string types (CHAR, VARCHAR, TEXT) the rest of buffer
+ is padded by zero byte.
+
+ NOTES
+ For variable length character fields (i.e. UTF-8) the "length"
+ parameter means a number of output buffer bytes as if all field
+ characters have maximal possible size (mbmaxlen). In the other words,
+ "length" parameter is a number of characters multiplied by
+ field_charset->mbmaxlen.
+
+ RETURN
+ Number of copied bytes (excluding padded zero bytes -- see above).
+ */
+
+ virtual uint get_key_image(char *buff, uint length, imagetype type)
+ {
+ get_image(buff, length, &my_charset_bin);
+ return length;
+ }
virtual void set_key_image(char *buff,uint length)
{ set_image(buff,length, &my_charset_bin); }
inline longlong val_int_offset(uint row_offset)
@@ -476,6 +507,7 @@ public:
friend class create_field;
my_decimal *val_decimal(my_decimal *);
virtual bool str_needs_quotes() { return TRUE; }
+ bool compare_str_field_flags(create_field *new_field, uint32 flags);
uint is_equal(create_field *new_field);
};
@@ -1133,6 +1165,7 @@ public:
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
Field *new_field(MEM_ROOT *root, struct st_table *new_table, bool keep_type);
+ virtual uint get_key_image(char *buff,uint length, imagetype type);
};
@@ -1185,7 +1218,7 @@ public:
return cmp_max(a, b, ~0L);
}
void sort_string(char *buff,uint length);
- void get_key_image(char *buff,uint length, imagetype type);
+ uint get_key_image(char *buff,uint length, imagetype type);
void set_key_image(char *buff,uint length);
void sql_type(String &str) const;
char *pack(char *to, const char *from, uint max_length=~(uint) 0);
@@ -1297,7 +1330,7 @@ public:
store_length(length);
memcpy_fixed(ptr+packlength,&data,sizeof(char*));
}
- void get_key_image(char *buff,uint length, imagetype type);
+ uint get_key_image(char *buff,uint length, imagetype type);
void set_key_image(char *buff,uint length);
void sql_type(String &str) const;
inline bool copy()
@@ -1328,6 +1361,7 @@ public:
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
uint32 max_display_length();
+ uint is_equal(create_field *new_field);
};
@@ -1354,7 +1388,7 @@ public:
int store(double nr);
int store(longlong nr, bool unsigned_val);
int store_decimal(const my_decimal *);
- void get_key_image(char *buff,uint length,imagetype type);
+ uint get_key_image(char *buff,uint length,imagetype type);
uint size_of() const { return sizeof(*this); }
int reset(void) { return !maybe_null() || Field_blob::reset(); }
};
@@ -1478,7 +1512,7 @@ public:
{ return cmp_binary((char *) a, (char *) b); }
int key_cmp(const byte *str, uint length);
int cmp_offset(uint row_offset);
- void get_key_image(char *buff, uint length, imagetype type);
+ uint get_key_image(char *buff, uint length, imagetype type);
void set_key_image(char *buff, uint length)
{ Field_bit::store(buff, length, &my_charset_bin); }
void sort_string(char *buff, uint length)
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 8fd9dd0354c..5bbaa793939 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -30,7 +30,7 @@
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
#include "ha_ndbcluster.h"
#include <ndbapi/NdbApi.hpp>
-#include <ndbapi/NdbScanFilter.hpp>
+#include "ha_ndbcluster_cond.h"
#include <../util/Bitmask.hpp>
#include <ndbapi/NdbIndexStat.hpp>
@@ -650,6 +650,26 @@ static bool ndb_supported_type(enum_field_types type)
/*
+ Check if MySQL field type forces var part in ndb storage
+*/
+static bool field_type_forces_var_part(enum_field_types type)
+{
+ switch (type) {
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ return TRUE;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ return FALSE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
Instruct NDB to set the value of the hidden primary key
*/
@@ -2403,7 +2423,7 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key,
if (!restart)
{
- if (generate_scan_filter(m_cond_stack, op))
+ if (m_cond && m_cond->generate_scan_filter(op))
DBUG_RETURN(ndb_err(trans));
if ((res= define_read_attrs(buf, op)))
@@ -2509,8 +2529,14 @@ int ha_ndbcluster::unique_index_scan(const KEY* key_info,
(get_ndb_partition_id(op)))
ERR_RETURN(trans->getNdbError());
}
-
- if (generate_scan_filter_from_key(op, key_info, key, key_len, buf))
+ if (!m_cond)
+ m_cond= new ha_ndbcluster_cond;
+ if (!m_cond)
+ {
+ my_errno= HA_ERR_OUT_OF_MEM;
+ DBUG_RETURN(my_errno);
+ }
+ if (m_cond->generate_scan_filter_from_key(op, key_info, key, key_len, buf))
DBUG_RETURN(ndb_err(trans));
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
@@ -2579,7 +2605,7 @@ int ha_ndbcluster::full_table_scan(byte *buf)
ERR_RETURN(trans->getNdbError());
}
- if (generate_scan_filter(m_cond_stack, op))
+ if (m_cond && m_cond->generate_scan_filter(op))
DBUG_RETURN(ndb_err(trans));
if ((res= define_read_attrs(buf, op)))
DBUG_RETURN(res);
@@ -3969,7 +3995,11 @@ int ha_ndbcluster::extra(enum ha_extra_function operation)
int ha_ndbcluster::reset()
{
DBUG_ENTER("ha_ndbcluster::reset");
- cond_clear();
+ if (m_cond)
+ {
+ m_cond->cond_clear();
+ }
+
/*
Regular partition pruning will set the bitmap appropriately.
Some queries like ALTER TABLE doesn't use partition pruning and
@@ -4997,6 +5027,9 @@ int ha_ndbcluster::create(const char *name,
my_free((char*)data, MYF(0));
my_free((char*)pack_data, MYF(0));
+ /*
+ Check for disk options
+ */
if (create_info->storage_media == HA_SM_DISK)
{
if (create_info->tablespace)
@@ -5013,13 +5046,38 @@ int ha_ndbcluster::create(const char *name,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
"TABLESPACE currently only supported for "
- "STORAGE DISK");
+ "STORAGE DISK");
DBUG_RETURN(HA_ERR_UNSUPPORTED);
}
tab.setTablespaceName(create_info->tablespace);
create_info->storage_media = HA_SM_DISK; //if use tablespace, that also means store on disk
}
+ /*
+ Handle table row type
+
+ Default is to let table rows have var part reference so that online
+ add column can be performed in the future. Explicitly setting row
+ type to fixed will omit var part reference, which will save data
+ memory in ndb, but at the cost of not being able to online add
+ column to this table
+ */
+ switch (create_info->row_type) {
+ case ROW_TYPE_FIXED:
+ tab.setForceVarPart(FALSE);
+ break;
+ case ROW_TYPE_DYNAMIC:
+ /* fall through, treat as default */
+ default:
+ /* fall through, treat as default */
+ case ROW_TYPE_DEFAULT:
+ tab.setForceVarPart(TRUE);
+ break;
+ }
+
+ /*
+ Setup columns
+ */
for (i= 0; i < form->s->fields; i++)
{
Field *field= form->field[i];
@@ -5029,12 +5087,32 @@ int ha_ndbcluster::create(const char *name,
if ((my_errno= create_ndb_column(col, field, create_info)))
DBUG_RETURN(my_errno);
- if (create_info->storage_media == HA_SM_DISK ||
- create_info->tablespace)
+ if (create_info->storage_media == HA_SM_DISK)
col.setStorageType(NdbDictionary::Column::StorageTypeDisk);
else
col.setStorageType(NdbDictionary::Column::StorageTypeMemory);
+ switch (create_info->row_type) {
+ case ROW_TYPE_FIXED:
+ if (field_type_forces_var_part(field->type()))
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ ndbcluster_hton_name,
+ "Row format FIXED incompatible with "
+ "variable sized attribute");
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ }
+ break;
+ case ROW_TYPE_DYNAMIC:
+ /*
+ Future: make columns dynamic in this case
+ */
+ break;
+ default:
+ break;
+ }
if (tab.addColumn(col))
{
DBUG_RETURN(my_errno= errno);
@@ -6036,7 +6114,7 @@ ha_ndbcluster::ha_ndbcluster(handlerton *hton, TABLE_SHARE *table_arg):
m_force_send(TRUE),
m_autoincrement_prefetch((ha_rows) 32),
m_transaction_on(TRUE),
- m_cond_stack(NULL),
+ m_cond(NULL),
m_multi_cursor(NULL)
{
int i;
@@ -6095,9 +6173,13 @@ ha_ndbcluster::~ha_ndbcluster()
}
DBUG_ASSERT(m_active_trans == NULL);
- // Discard the condition stack
- DBUG_PRINT("info", ("Clearing condition stack"));
- cond_clear();
+ // Discard any generated condition
+ DBUG_PRINT("info", ("Deleting generated condition"));
+ if (m_cond)
+ {
+ delete m_cond;
+ m_cond= NULL;
+ }
DBUG_VOID_RETURN;
}
@@ -8112,7 +8194,7 @@ ndb_get_table_statistics(ha_ndbcluster* file, bool report_error, Ndb* ndb, const
NdbError error;
int retries= 10;
int reterr= 0;
- int retry_sleep= 30 * 1000; /* 30 milliseconds */
+ int retry_sleep= 30; /* 30 milliseconds, transaction */
#ifndef DBUG_OFF
char buff[22], buff2[22], buff3[22], buff4[22];
#endif
@@ -8469,7 +8551,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab))
&&!scanOp->readTuples(lm, 0, parallelism, sorted,
FALSE, TRUE, need_pk, TRUE)
- &&!generate_scan_filter(m_cond_stack, scanOp)
+ &&!(m_cond && m_cond->generate_scan_filter(scanOp))
&&!define_read_attrs(end_of_buffer-reclength, scanOp))
{
m_multi_cursor= scanOp;
@@ -9055,28 +9137,15 @@ COND*
ha_ndbcluster::cond_push(const COND *cond)
{
DBUG_ENTER("cond_push");
- Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
- if (ndb_cond == NULL)
+ if (!m_cond)
+ m_cond= new ha_ndbcluster_cond;
+ if (!m_cond)
{
my_errno= HA_ERR_OUT_OF_MEM;
DBUG_RETURN(NULL);
}
DBUG_EXECUTE("where",print_where((COND *)cond, m_tabname););
- if (m_cond_stack)
- ndb_cond->next= m_cond_stack;
- else
- ndb_cond->next= NULL;
- m_cond_stack= ndb_cond;
-
- if (serialize_cond(cond, ndb_cond))
- {
- DBUG_RETURN(NULL);
- }
- else
- {
- cond_pop();
- }
- DBUG_RETURN(cond);
+ DBUG_RETURN(m_cond->cond_push(cond, table, (NDBTAB *)m_table));
}
/*
@@ -9085,1356 +9154,8 @@ ha_ndbcluster::cond_push(const COND *cond)
void
ha_ndbcluster::cond_pop()
{
- Ndb_cond_stack *ndb_cond_stack= m_cond_stack;
- if (ndb_cond_stack)
- {
- m_cond_stack= ndb_cond_stack->next;
- delete ndb_cond_stack;
- }
-}
-
-/*
- Clear the condition stack
-*/
-void
-ha_ndbcluster::cond_clear()
-{
- DBUG_ENTER("cond_clear");
- while (m_cond_stack)
- cond_pop();
-
- DBUG_VOID_RETURN;
-}
-
-/*
- Serialize the item tree into a linked list represented by Ndb_cond
- for fast generation of NbdScanFilter. Adds information such as
- position of fields that is not directly available in the Item tree.
- Also checks if condition is supported.
-*/
-void ndb_serialize_cond(const Item *item, void *arg)
-{
- Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
- DBUG_ENTER("ndb_serialize_cond");
-
- // Check if we are skipping arguments to a function to be evaluated
- if (context->skip)
- {
- DBUG_PRINT("info", ("Skiping argument %d", context->skip));
- context->skip--;
- switch (item->type()) {
- case Item::FUNC_ITEM:
- {
- Item_func *func_item= (Item_func *) item;
- context->skip+= func_item->argument_count();
- break;
- }
- case Item::INT_ITEM:
- case Item::REAL_ITEM:
- case Item::STRING_ITEM:
- case Item::VARBIN_ITEM:
- case Item::DECIMAL_ITEM:
- break;
- default:
- context->supported= FALSE;
- break;
- }
-
- DBUG_VOID_RETURN;
- }
-
- if (context->supported)
- {
- Ndb_rewrite_context *rewrite_context2= context->rewrite_stack;
- const Item_func *rewrite_func_item;
- // Check if we are rewriting some unsupported function call
- if (rewrite_context2 &&
- (rewrite_func_item= rewrite_context2->func_item) &&
- rewrite_context2->count++ == 0)
- {
- switch (rewrite_func_item->functype()) {
- case Item_func::BETWEEN:
- /*
- Rewrite
- <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
- to <field>|<const> > <const1>|<field1> AND
- <field>|<const> < <const2>|<field2>
- or actually in prefix format
- BEGIN(AND) GT(<field>|<const>, <const1>|<field1>),
- LT(<field>|<const>, <const2>|<field2>), END()
- */
- case Item_func::IN_FUNC:
- {
- /*
- Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
- to <field>|<const> = <const1>|<field1> OR
- <field> = <const2>|<field2> ...
- or actually in prefix format
- BEGIN(OR) EQ(<field>|<const>, <const1><field1>),
- EQ(<field>|<const>, <const2>|<field2>), ... END()
- Each part of the disjunction is added for each call
- to ndb_serialize_cond and end of rewrite statement
- is wrapped in end of ndb_serialize_cond
- */
- if (context->expecting(item->type()))
- {
- // This is the <field>|<const> item, save it in the rewrite context
- rewrite_context2->left_hand_item= item;
- if (item->type() == Item::FUNC_ITEM)
- {
- Item_func *func_item= (Item_func *) item;
- if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
- func_item->const_item())
- {
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- }
- else
- {
- DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
- context->supported= FALSE;
- DBUG_VOID_RETURN;
-
- }
- }
- }
- else
- {
- // Non-supported BETWEEN|IN expression
- DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
- item->type()));
- context->supported= FALSE;
- DBUG_VOID_RETURN;
- }
- break;
- }
- default:
- context->supported= FALSE;
- break;
- }
- DBUG_VOID_RETURN;
- }
- else
- {
- Ndb_cond_stack *ndb_stack= context->stack_ptr;
- Ndb_cond *prev_cond= context->cond_ptr;
- Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond();
- if (!ndb_stack->ndb_cond)
- ndb_stack->ndb_cond= curr_cond;
- curr_cond->prev= prev_cond;
- if (prev_cond) prev_cond->next= curr_cond;
- // Check if we are rewriting some unsupported function call
- if (context->rewrite_stack)
- {
- Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
- const Item_func *func_item= rewrite_context->func_item;
- switch (func_item->functype()) {
- case Item_func::BETWEEN:
- {
- /*
- Rewrite
- <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
- to <field>|<const> > <const1>|<field1> AND
- <field>|<const> < <const2>|<field2>
- or actually in prefix format
- BEGIN(AND) GT(<field>|<const>, <const1>|<field1>),
- LT(<field>|<const>, <const2>|<field2>), END()
- */
- if (rewrite_context->count == 2)
- {
- // Lower limit of BETWEEN
- DBUG_PRINT("info", ("GE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::GE_FUNC, 2);
- }
- else if (rewrite_context->count == 3)
- {
- // Upper limit of BETWEEN
- DBUG_PRINT("info", ("LE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::LE_FUNC, 2);
- }
- else
- {
- // Illegal BETWEEN expression
- DBUG_PRINT("info", ("Illegal BETWEEN expression"));
- context->supported= FALSE;
- DBUG_VOID_RETURN;
- }
- break;
- }
- case Item_func::IN_FUNC:
- {
- /*
- Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
- to <field>|<const> = <const1>|<field1> OR
- <field> = <const2>|<field2> ...
- or actually in prefix format
- BEGIN(OR) EQ(<field>|<const>, <const1><field1>),
- EQ(<field>|<const>, <const2>|<field2>), ... END()
- Each part of the disjunction is added for each call
- to ndb_serialize_cond and end of rewrite statement
- is wrapped in end of ndb_serialize_cond
- */
- DBUG_PRINT("info", ("EQ_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::EQ_FUNC, 2);
- break;
- }
- default:
- context->supported= FALSE;
- }
- // Handle left hand <field>|<const>
- context->rewrite_stack= NULL; // Disable rewrite mode
- context->expect_only(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- context->expect(Item::INT_ITEM);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FUNC_ITEM);
- ndb_serialize_cond(rewrite_context->left_hand_item, arg);
- context->skip= 0; // Any FUNC_ITEM expression has already been parsed
- context->rewrite_stack= rewrite_context; // Enable rewrite mode
- if (!context->supported)
- DBUG_VOID_RETURN;
-
- prev_cond= context->cond_ptr;
- curr_cond= context->cond_ptr= new Ndb_cond();
- prev_cond->next= curr_cond;
- }
-
- // Check for end of AND/OR expression
- if (!item)
- {
- // End marker for condition group
- DBUG_PRINT("info", ("End of condition group"));
- curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
- }
- else
- {
- switch (item->type()) {
- case Item::FIELD_ITEM:
- {
- Item_field *field_item= (Item_field *) item;
- Field *field= field_item->field;
- enum_field_types type= field->type();
- /*
- Check that the field is part of the table of the handler
- instance and that we expect a field with of this result type.
- */
- if (context->table->s == field->table->s)
- {
- const NDBTAB *tab= (const NDBTAB *) context->ndb_table;
- DBUG_PRINT("info", ("FIELD_ITEM"));
- DBUG_PRINT("info", ("table %s", tab->getName()));
- DBUG_PRINT("info", ("column %s", field->field_name));
- DBUG_PRINT("info", ("type %d", field->type()));
- DBUG_PRINT("info", ("result type %d", field->result_type()));
-
- // Check that we are expecting a field and with the correct
- // result type
- if (context->expecting(Item::FIELD_ITEM) &&
- context->expecting_field_type(field->type()) &&
- (context->expecting_field_result(field->result_type()) ||
- // Date and year can be written as string or int
- ((type == MYSQL_TYPE_TIME ||
- type == MYSQL_TYPE_DATE ||
- type == MYSQL_TYPE_YEAR ||
- type == MYSQL_TYPE_DATETIME)
- ? (context->expecting_field_result(STRING_RESULT) ||
- context->expecting_field_result(INT_RESULT))
- : TRUE)) &&
- // Bit fields no yet supported in scan filter
- type != MYSQL_TYPE_BIT &&
- // No BLOB support in scan filter
- type != MYSQL_TYPE_TINY_BLOB &&
- type != MYSQL_TYPE_MEDIUM_BLOB &&
- type != MYSQL_TYPE_LONG_BLOB &&
- type != MYSQL_TYPE_BLOB)
- {
- const NDBCOL *col= tab->getColumn(field->field_name);
- DBUG_ASSERT(col);
- curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo());
- context->dont_expect(Item::FIELD_ITEM);
- context->expect_no_field_result();
- if (! context->expecting_nothing())
- {
- // We have not seen second argument yet
- if (type == MYSQL_TYPE_TIME ||
- type == MYSQL_TYPE_DATE ||
- type == MYSQL_TYPE_YEAR ||
- type == MYSQL_TYPE_DATETIME)
- {
- context->expect_only(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- }
- else
- switch (field->result_type()) {
- case STRING_RESULT:
- // Expect char string or binary string
- context->expect_only(Item::STRING_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect_collation(field_item->collation.collation);
- break;
- case REAL_RESULT:
- context->expect_only(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::INT_ITEM);
- break;
- case INT_RESULT:
- context->expect_only(Item::INT_ITEM);
- context->expect(Item::VARBIN_ITEM);
- break;
- case DECIMAL_RESULT:
- context->expect_only(Item::DECIMAL_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::INT_ITEM);
- break;
- default:
- break;
- }
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- // Check that field and string constant collations are the same
- if ((field->result_type() == STRING_RESULT) &&
- !context->expecting_collation(item->collation.collation)
- && type != MYSQL_TYPE_TIME
- && type != MYSQL_TYPE_DATE
- && type != MYSQL_TYPE_YEAR
- && type != MYSQL_TYPE_DATETIME)
- {
- DBUG_PRINT("info", ("Found non-matching collation %s",
- item->collation.collation->name));
- context->supported= FALSE;
- }
- }
- break;
- }
- else
- {
- DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
- field->result_type(), type));
- context->supported= FALSE;
- }
- }
- else
- {
- DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
- context->table->s->table_name.str,
- field->table->s->table_name.str));
- context->supported= FALSE;
- }
- break;
- }
- case Item::FUNC_ITEM:
- {
- Item_func *func_item= (Item_func *) item;
- // Check that we expect a function or functional expression here
- if (context->expecting(Item::FUNC_ITEM) ||
- func_item->functype() == Item_func::UNKNOWN_FUNC)
- context->expect_nothing();
- else
- {
- // Did not expect function here
- context->supported= FALSE;
- break;
- }
-
- switch (func_item->functype()) {
- case Item_func::EQ_FUNC:
- {
- DBUG_PRINT("info", ("EQ_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::NE_FUNC:
- {
- DBUG_PRINT("info", ("NE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::LT_FUNC:
- {
- DBUG_PRINT("info", ("LT_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::LE_FUNC:
- {
- DBUG_PRINT("info", ("LE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::GE_FUNC:
- {
- DBUG_PRINT("info", ("GE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::GT_FUNC:
- {
- DBUG_PRINT("info", ("GT_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::REAL_ITEM);
- context->expect(Item::DECIMAL_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::LIKE_FUNC:
- {
- DBUG_PRINT("info", ("LIKE_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::FIELD_ITEM);
- context->expect_only_field_type(MYSQL_TYPE_STRING);
- context->expect_field_type(MYSQL_TYPE_VAR_STRING);
- context->expect_field_type(MYSQL_TYPE_VARCHAR);
- context->expect_field_result(STRING_RESULT);
- context->expect(Item::FUNC_ITEM);
- break;
- }
- case Item_func::ISNULL_FUNC:
- {
- DBUG_PRINT("info", ("ISNULL_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::ISNOTNULL_FUNC:
- {
- DBUG_PRINT("info", ("ISNOTNULL_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::FIELD_ITEM);
- context->expect_field_result(STRING_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(INT_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- break;
- }
- case Item_func::NOT_FUNC:
- {
- DBUG_PRINT("info", ("NOT_FUNC"));
- curr_cond->ndb_item= new Ndb_item(func_item->functype(),
- func_item);
- context->expect(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- break;
- }
- case Item_func::BETWEEN:
- {
- DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
- Item_func_between *between_func= (Item_func_between *) func_item;
- Ndb_rewrite_context *rewrite_context=
- new Ndb_rewrite_context(func_item);
- rewrite_context->next= context->rewrite_stack;
- context->rewrite_stack= rewrite_context;
- if (between_func->negated)
- {
- DBUG_PRINT("info", ("NOT_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
- prev_cond= curr_cond;
- curr_cond= context->cond_ptr= new Ndb_cond();
- curr_cond->prev= prev_cond;
- prev_cond->next= curr_cond;
- }
- DBUG_PRINT("info", ("COND_AND_FUNC"));
- curr_cond->ndb_item=
- new Ndb_item(Item_func::COND_AND_FUNC,
- func_item->argument_count() - 1);
- context->expect_only(Item::FIELD_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FUNC_ITEM);
- break;
- }
- case Item_func::IN_FUNC:
- {
- DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
- Item_func_in *in_func= (Item_func_in *) func_item;
- Ndb_rewrite_context *rewrite_context=
- new Ndb_rewrite_context(func_item);
- rewrite_context->next= context->rewrite_stack;
- context->rewrite_stack= rewrite_context;
- if (in_func->negated)
- {
- DBUG_PRINT("info", ("NOT_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
- prev_cond= curr_cond;
- curr_cond= context->cond_ptr= new Ndb_cond();
- curr_cond->prev= prev_cond;
- prev_cond->next= curr_cond;
- }
- DBUG_PRINT("info", ("COND_OR_FUNC"));
- curr_cond->ndb_item= new Ndb_item(Item_func::COND_OR_FUNC,
- func_item->argument_count() - 1);
- context->expect_only(Item::FIELD_ITEM);
- context->expect(Item::INT_ITEM);
- context->expect(Item::STRING_ITEM);
- context->expect(Item::VARBIN_ITEM);
- context->expect(Item::FUNC_ITEM);
- break;
- }
- case Item_func::UNKNOWN_FUNC:
- {
- DBUG_PRINT("info", ("UNKNOWN_FUNC %s",
- func_item->const_item()?"const":""));
- DBUG_PRINT("info", ("result type %d", func_item->result_type()));
- if (func_item->const_item())
- {
- switch (func_item->result_type()) {
- case STRING_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::STRING_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(STRING_RESULT);
- context->expect_collation(func_item->collation.collation);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- // Check that string result have correct collation
- if (!context->expecting_collation(item->collation.collation))
- {
- DBUG_PRINT("info", ("Found non-matching collation %s",
- item->collation.collation->name));
- context->supported= FALSE;
- }
- }
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case REAL_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::REAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(REAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
-
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case INT_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::INT_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(INT_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
-
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- case DECIMAL_RESULT:
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::DECIMAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(DECIMAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- // Skip any arguments since we will evaluate function instead
- DBUG_PRINT("info", ("Skip until end of arguments marker"));
- context->skip= func_item->argument_count();
- break;
- }
- default:
- break;
- }
- }
- else
- // Function does not return constant expression
- context->supported= FALSE;
- break;
- }
- default:
- {
- DBUG_PRINT("info", ("Found func_item of type %d",
- func_item->functype()));
- context->supported= FALSE;
- }
- }
- break;
- }
- case Item::STRING_ITEM:
- DBUG_PRINT("info", ("STRING_ITEM"));
- if (context->expecting(Item::STRING_ITEM))
- {
-#ifndef DBUG_OFF
- char buff[256];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.length(0);
- Item_string *string_item= (Item_string *) item;
- DBUG_PRINT("info", ("value \"%s\"",
- string_item->val_str(&str)->ptr()));
-#endif
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::STRING_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(STRING_RESULT);
- context->expect_collation(item->collation.collation);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- // Check that we are comparing with a field with same collation
- if (!context->expecting_collation(item->collation.collation))
- {
- DBUG_PRINT("info", ("Found non-matching collation %s",
- item->collation.collation->name));
- context->supported= FALSE;
- }
- }
- }
- else
- context->supported= FALSE;
- break;
- case Item::INT_ITEM:
- DBUG_PRINT("info", ("INT_ITEM"));
- if (context->expecting(Item::INT_ITEM))
- {
- DBUG_PRINT("info", ("value %ld",
- (long) ((Item_int*) item)->value));
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::INT_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(INT_RESULT);
- context->expect_field_result(REAL_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- }
- else
- context->supported= FALSE;
- break;
- case Item::REAL_ITEM:
- DBUG_PRINT("info", ("REAL_ITEM"));
- if (context->expecting(Item::REAL_ITEM))
- {
- DBUG_PRINT("info", ("value %f", ((Item_float*) item)->value));
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::REAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(REAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- }
- else
- context->supported= FALSE;
- break;
- case Item::VARBIN_ITEM:
- DBUG_PRINT("info", ("VARBIN_ITEM"));
- if (context->expecting(Item::VARBIN_ITEM))
- {
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::VARBIN_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(STRING_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- }
- else
- context->supported= FALSE;
- break;
- case Item::DECIMAL_ITEM:
- DBUG_PRINT("info", ("DECIMAL_ITEM"));
- if (context->expecting(Item::DECIMAL_ITEM))
- {
- DBUG_PRINT("info", ("value %f",
- ((Item_decimal*) item)->val_real()));
- NDB_ITEM_QUALIFICATION q;
- q.value_type= Item::DECIMAL_ITEM;
- curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
- if (! context->expecting_no_field_result())
- {
- // We have not seen the field argument yet
- context->expect_only(Item::FIELD_ITEM);
- context->expect_only_field_result(REAL_RESULT);
- context->expect_field_result(DECIMAL_RESULT);
- }
- else
- {
- // Expect another logical expression
- context->expect_only(Item::FUNC_ITEM);
- context->expect(Item::COND_ITEM);
- }
- }
- else
- context->supported= FALSE;
- break;
- case Item::COND_ITEM:
- {
- Item_cond *cond_item= (Item_cond *) item;
-
- if (context->expecting(Item::COND_ITEM))
- {
- switch (cond_item->functype()) {
- case Item_func::COND_AND_FUNC:
- DBUG_PRINT("info", ("COND_AND_FUNC"));
- curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
- cond_item);
- break;
- case Item_func::COND_OR_FUNC:
- DBUG_PRINT("info", ("COND_OR_FUNC"));
- curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
- cond_item);
- break;
- default:
- DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype()));
- context->supported= FALSE;
- break;
- }
- }
- else
- {
- /* Did not expect condition */
- context->supported= FALSE;
- }
- break;
- }
- default:
- {
- DBUG_PRINT("info", ("Found item of type %d", item->type()));
- context->supported= FALSE;
- }
- }
- }
- if (context->supported && context->rewrite_stack)
- {
- Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
- if (rewrite_context->count ==
- rewrite_context->func_item->argument_count())
- {
- // Rewrite is done, wrap an END() at the en
- DBUG_PRINT("info", ("End of condition group"));
- prev_cond= curr_cond;
- curr_cond= context->cond_ptr= new Ndb_cond();
- curr_cond->prev= prev_cond;
- prev_cond->next= curr_cond;
- curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
- // Pop rewrite stack
- context->rewrite_stack= rewrite_context->next;
- rewrite_context->next= NULL;
- delete(rewrite_context);
- }
- }
- }
- }
-
- DBUG_VOID_RETURN;
-}
-
-bool
-ha_ndbcluster::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond)
-{
- DBUG_ENTER("serialize_cond");
- Item *item= (Item *) cond;
- Ndb_cond_traverse_context context(table, (void *)m_table, ndb_cond);
- // Expect a logical expression
- context.expect(Item::FUNC_ITEM);
- context.expect(Item::COND_ITEM);
- item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
- DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
-
- DBUG_RETURN(context.supported);
-}
-
-int
-ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond,
- NdbScanFilter *filter,
- bool negated)
-{
- DBUG_ENTER("build_scan_filter_predicate");
- switch (cond->ndb_item->type) {
- case NDB_FUNCTION:
- {
- if (!cond->next)
- break;
- Ndb_item *a= cond->next->ndb_item;
- Ndb_item *b, *field, *value= NULL;
-
- switch (cond->ndb_item->argument_count()) {
- case 1:
- field= (a->type == NDB_FIELD)? a : NULL;
- break;
- case 2:
- if (!cond->next->next)
- {
- field= NULL;
- break;
- }
- b= cond->next->next->ndb_item;
- value= ((a->type == NDB_VALUE) ? a :
- (b->type == NDB_VALUE) ? b :
- NULL);
- field= ((a->type == NDB_FIELD) ? a :
- (b->type == NDB_FIELD) ? b :
- NULL);
- break;
- default:
- field= NULL; //Keep compiler happy
- DBUG_ASSERT(0);
- break;
- }
- switch ((negated) ?
- Ndb_item::negate(cond->ndb_item->qualification.function_type)
- : cond->ndb_item->qualification.function_type) {
- case NDB_EQ_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- DBUG_PRINT("info", ("Generating EQ filter"));
- if (filter->cmp(NdbScanFilter::COND_EQ,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_NE_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- DBUG_PRINT("info", ("Generating NE filter"));
- if (filter->cmp(NdbScanFilter::COND_NE,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_LT_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- if (a == field)
- {
- DBUG_PRINT("info", ("Generating LT filter"));
- if (filter->cmp(NdbScanFilter::COND_LT,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- else
- {
- DBUG_PRINT("info", ("Generating GT filter"));
- if (filter->cmp(NdbScanFilter::COND_GT,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_LE_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- if (a == field)
- {
- DBUG_PRINT("info", ("Generating LE filter"));
- if (filter->cmp(NdbScanFilter::COND_LE,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- else
- {
- DBUG_PRINT("info", ("Generating GE filter"));
- if (filter->cmp(NdbScanFilter::COND_GE,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_GE_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- if (a == field)
- {
- DBUG_PRINT("info", ("Generating GE filter"));
- if (filter->cmp(NdbScanFilter::COND_GE,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- else
- {
- DBUG_PRINT("info", ("Generating LE filter"));
- if (filter->cmp(NdbScanFilter::COND_LE,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_GT_FUNC:
- {
- if (!value || !field) break;
- // Save value in right format for the field type
- value->save_in_field(field);
- if (a == field)
- {
- DBUG_PRINT("info", ("Generating GT filter"));
- if (filter->cmp(NdbScanFilter::COND_GT,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- else
- {
- DBUG_PRINT("info", ("Generating LT filter"));
- if (filter->cmp(NdbScanFilter::COND_LT,
- field->get_field_no(),
- field->get_val(),
- field->pack_length()) == -1)
- DBUG_RETURN(1);
- }
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_LIKE_FUNC:
- {
- if (!value || !field) break;
- if ((value->qualification.value_type != Item::STRING_ITEM) &&
- (value->qualification.value_type != Item::VARBIN_ITEM))
- break;
- // Save value in right format for the field type
- value->save_in_field(field);
- DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)",
- field->get_field_no(), value->get_val(),
- value->pack_length()));
- if (filter->cmp(NdbScanFilter::COND_LIKE,
- field->get_field_no(),
- value->get_val(),
- value->pack_length()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_NOTLIKE_FUNC:
- {
- if (!value || !field) break;
- if ((value->qualification.value_type != Item::STRING_ITEM) &&
- (value->qualification.value_type != Item::VARBIN_ITEM))
- break;
- // Save value in right format for the field type
- value->save_in_field(field);
- DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)",
- field->get_field_no(), value->get_val(),
- value->pack_length()));
- if (filter->cmp(NdbScanFilter::COND_NOT_LIKE,
- field->get_field_no(),
- value->get_val(),
- value->pack_length()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next->next;
- DBUG_RETURN(0);
- }
- case NDB_ISNULL_FUNC:
- if (!field)
- break;
- DBUG_PRINT("info", ("Generating ISNULL filter"));
- if (filter->isnull(field->get_field_no()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next;
- DBUG_RETURN(0);
- case NDB_ISNOTNULL_FUNC:
- {
- if (!field)
- break;
- DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
- if (filter->isnotnull(field->get_field_no()) == -1)
- DBUG_RETURN(1);
- cond= cond->next->next;
- DBUG_RETURN(0);
- }
- default:
- break;
- }
- break;
- }
- default:
- break;
- }
- DBUG_PRINT("info", ("Found illegal condition"));
- DBUG_RETURN(1);
-}
-
-
-int
-ha_ndbcluster::build_scan_filter_group(Ndb_cond* &cond, NdbScanFilter *filter)
-{
- uint level=0;
- bool negated= FALSE;
- DBUG_ENTER("build_scan_filter_group");
-
- do
- {
- if (!cond)
- DBUG_RETURN(1);
- switch (cond->ndb_item->type) {
- case NDB_FUNCTION:
- {
- switch (cond->ndb_item->qualification.function_type) {
- case NDB_COND_AND_FUNC:
- {
- level++;
- DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
- level));
- if ((negated) ? filter->begin(NdbScanFilter::NAND)
- : filter->begin(NdbScanFilter::AND) == -1)
- DBUG_RETURN(1);
- negated= FALSE;
- cond= cond->next;
- break;
- }
- case NDB_COND_OR_FUNC:
- {
- level++;
- DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR",
- level));
- if ((negated) ? filter->begin(NdbScanFilter::NOR)
- : filter->begin(NdbScanFilter::OR) == -1)
- DBUG_RETURN(1);
- negated= FALSE;
- cond= cond->next;
- break;
- }
- case NDB_NOT_FUNC:
- {
- DBUG_PRINT("info", ("Generating negated query"));
- cond= cond->next;
- negated= TRUE;
- break;
- }
- default:
- if (build_scan_filter_predicate(cond, filter, negated))
- DBUG_RETURN(1);
- negated= FALSE;
- break;
- }
- break;
- }
- case NDB_END_COND:
- DBUG_PRINT("info", ("End of group %u", level));
- level--;
- if (cond) cond= cond->next;
- if (filter->end() == -1)
- DBUG_RETURN(1);
- if (!negated)
- break;
- // else fall through (NOT END is an illegal condition)
- default:
- {
- DBUG_PRINT("info", ("Illegal scan filter"));
- }
- }
- } while (level > 0 || negated);
-
- DBUG_RETURN(0);
-}
-
-
-int
-ha_ndbcluster::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
-{
- bool simple_cond= TRUE;
- DBUG_ENTER("build_scan_filter");
-
- switch (cond->ndb_item->type) {
- case NDB_FUNCTION:
- switch (cond->ndb_item->qualification.function_type) {
- case NDB_COND_AND_FUNC:
- case NDB_COND_OR_FUNC:
- simple_cond= FALSE;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- if (simple_cond && filter->begin() == -1)
- DBUG_RETURN(1);
- if (build_scan_filter_group(cond, filter))
- DBUG_RETURN(1);
- if (simple_cond && filter->end() == -1)
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
-}
-
-int
-ha_ndbcluster::generate_scan_filter(Ndb_cond_stack *ndb_cond_stack,
- NdbScanOperation *op)
-{
- DBUG_ENTER("generate_scan_filter");
-
- if (ndb_cond_stack)
- {
- NdbScanFilter filter(op);
-
- DBUG_RETURN(generate_scan_filter_from_cond(ndb_cond_stack, filter));
- }
- else
- {
- DBUG_PRINT("info", ("Empty stack"));
- }
-
- DBUG_RETURN(0);
-}
-
-
-int
-ha_ndbcluster::generate_scan_filter_from_cond(Ndb_cond_stack *ndb_cond_stack,
- NdbScanFilter& filter)
-{
- bool multiple_cond= FALSE;
- DBUG_ENTER("generate_scan_filter_from_cond");
-
- // Wrap an AND group around multiple conditions
- if (ndb_cond_stack->next)
- {
- multiple_cond= TRUE;
- if (filter.begin() == -1)
- DBUG_RETURN(1);
- }
- for (Ndb_cond_stack *stack= ndb_cond_stack;
- (stack);
- stack= stack->next)
- {
- Ndb_cond *cond= stack->ndb_cond;
-
- if (build_scan_filter(cond, &filter))
- {
- DBUG_PRINT("info", ("build_scan_filter failed"));
- DBUG_RETURN(1);
- }
- }
- if (multiple_cond && filter.end() == -1)
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
-}
-
-
-int ha_ndbcluster::generate_scan_filter_from_key(NdbScanOperation *op,
- const KEY* key_info,
- const byte *key,
- uint key_len,
- byte *buf)
-{
- KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
- NdbScanFilter filter(op);
- int res;
- DBUG_ENTER("generate_scan_filter_from_key");
-
- filter.begin(NdbScanFilter::AND);
- for (; key_part != end; key_part++)
- {
- Field* field= key_part->field;
- uint32 pack_len= field->pack_length();
- const byte* ptr= key;
- DBUG_PRINT("info", ("Filtering value for %s", field->field_name));
- DBUG_DUMP("key", (char*)ptr, pack_len);
- if (key_part->null_bit)
- {
- DBUG_PRINT("info", ("Generating ISNULL filter"));
- if (filter.isnull(key_part->fieldnr-1) == -1)
- DBUG_RETURN(1);
- }
- else
- {
- DBUG_PRINT("info", ("Generating EQ filter"));
- if (filter.cmp(NdbScanFilter::COND_EQ,
- key_part->fieldnr-1,
- ptr,
- pack_len) == -1)
- DBUG_RETURN(1);
- }
- key += key_part->store_length;
- }
- // Add any pushed condition
- if (m_cond_stack &&
- (res= generate_scan_filter_from_cond(m_cond_stack, filter)))
- DBUG_RETURN(res);
-
- if (filter.end() == -1)
- DBUG_RETURN(1);
-
- DBUG_RETURN(0);
+ if (m_cond)
+ m_cond->cond_pop();
}
diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h
index afc781e762c..df621ff1938 100644
--- a/sql/ha_ndbcluster.h
+++ b/sql/ha_ndbcluster.h
@@ -37,11 +37,11 @@ class NdbOperation; // Forward declaration
class NdbTransaction; // Forward declaration
class NdbRecAttr; // Forward declaration
class NdbScanOperation;
-class NdbScanFilter;
class NdbIndexScanOperation;
class NdbBlob;
class NdbIndexStat;
class NdbEventOperation;
+class ha_ndbcluster_cond;
// connectstring to cluster if given by mysqld
extern const char *ndbcluster_connectstring;
@@ -161,424 +161,6 @@ struct Ndb_tuple_id_range_guard {
#define NSF_NO_BINLOG 4 /* table should not be binlogged */
#endif
-typedef enum ndb_item_type {
- NDB_VALUE = 0, // Qualified more with Item::Type
- NDB_FIELD = 1, // Qualified from table definition
- NDB_FUNCTION = 2,// Qualified from Item_func::Functype
- NDB_END_COND = 3 // End marker for condition group
-} NDB_ITEM_TYPE;
-
-typedef enum ndb_func_type {
- NDB_EQ_FUNC = 0,
- NDB_NE_FUNC = 1,
- NDB_LT_FUNC = 2,
- NDB_LE_FUNC = 3,
- NDB_GT_FUNC = 4,
- NDB_GE_FUNC = 5,
- NDB_ISNULL_FUNC = 6,
- NDB_ISNOTNULL_FUNC = 7,
- NDB_LIKE_FUNC = 8,
- NDB_NOTLIKE_FUNC = 9,
- NDB_NOT_FUNC = 10,
- NDB_UNKNOWN_FUNC = 11,
- NDB_COND_AND_FUNC = 12,
- NDB_COND_OR_FUNC = 13,
- NDB_UNSUPPORTED_FUNC = 14
-} NDB_FUNC_TYPE;
-
-typedef union ndb_item_qualification {
- Item::Type value_type;
- enum_field_types field_type; // Instead of Item::FIELD_ITEM
- NDB_FUNC_TYPE function_type; // Instead of Item::FUNC_ITEM
-} NDB_ITEM_QUALIFICATION;
-
-typedef struct ndb_item_field_value {
- Field* field;
- int column_no;
-} NDB_ITEM_FIELD_VALUE;
-
-typedef union ndb_item_value {
- const Item *item;
- NDB_ITEM_FIELD_VALUE *field_value;
- uint arg_count;
-} NDB_ITEM_VALUE;
-
-struct negated_function_mapping
-{
- NDB_FUNC_TYPE pos_fun;
- NDB_FUNC_TYPE neg_fun;
-};
-
-
-/*
- Define what functions can be negated in condition pushdown.
- Note, these HAVE to be in the same order as in definition enum
-*/
-static const negated_function_mapping neg_map[]=
-{
- {NDB_EQ_FUNC, NDB_NE_FUNC},
- {NDB_NE_FUNC, NDB_EQ_FUNC},
- {NDB_LT_FUNC, NDB_GE_FUNC},
- {NDB_LE_FUNC, NDB_GT_FUNC},
- {NDB_GT_FUNC, NDB_LE_FUNC},
- {NDB_GE_FUNC, NDB_LT_FUNC},
- {NDB_ISNULL_FUNC, NDB_ISNOTNULL_FUNC},
- {NDB_ISNOTNULL_FUNC, NDB_ISNULL_FUNC},
- {NDB_LIKE_FUNC, NDB_NOTLIKE_FUNC},
- {NDB_NOTLIKE_FUNC, NDB_LIKE_FUNC},
- {NDB_NOT_FUNC, NDB_UNSUPPORTED_FUNC},
- {NDB_UNKNOWN_FUNC, NDB_UNSUPPORTED_FUNC},
- {NDB_COND_AND_FUNC, NDB_UNSUPPORTED_FUNC},
- {NDB_COND_OR_FUNC, NDB_UNSUPPORTED_FUNC},
- {NDB_UNSUPPORTED_FUNC, NDB_UNSUPPORTED_FUNC}
-};
-
-/*
- This class is the construction element for serialization of Item tree
- in condition pushdown.
- An instance of Ndb_Item represents a constant, table field reference,
- unary or binary comparison predicate, and start/end of AND/OR.
- Instances of Ndb_Item are stored in a linked list implemented by Ndb_cond
- class.
- The order of elements produced by Ndb_cond::next corresponds to
- breadth-first traversal of the Item (i.e. expression) tree in prefix order.
- AND and OR have arbitrary arity, so the end of AND/OR group is marked with
- Ndb_item with type == NDB_END_COND.
- NOT items represent negated conditions and generate NAND/NOR groups.
-*/
-class Ndb_item {
- public:
- Ndb_item(NDB_ITEM_TYPE item_type) : type(item_type) {};
- Ndb_item(NDB_ITEM_TYPE item_type,
- NDB_ITEM_QUALIFICATION item_qualification,
- const Item *item_value)
- : type(item_type), qualification(item_qualification)
- {
- switch(item_type) {
- case(NDB_VALUE):
- value.item= item_value;
- break;
- case(NDB_FIELD): {
- NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE();
- Item_field *field_item= (Item_field *) item_value;
- field_value->field= field_item->field;
- field_value->column_no= -1; // Will be fetched at scan filter generation
- value.field_value= field_value;
- break;
- }
- case(NDB_FUNCTION):
- value.item= item_value;
- value.arg_count= ((Item_func *) item_value)->argument_count();
- break;
- case(NDB_END_COND):
- break;
- }
- };
- Ndb_item(Field *field, int column_no) : type(NDB_FIELD)
- {
- NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE();
- qualification.field_type= field->type();
- field_value->field= field;
- field_value->column_no= column_no;
- value.field_value= field_value;
- };
- Ndb_item(Item_func::Functype func_type, const Item *item_value)
- : type(NDB_FUNCTION)
- {
- qualification.function_type= item_func_to_ndb_func(func_type);
- value.item= item_value;
- value.arg_count= ((Item_func *) item_value)->argument_count();
- };
- Ndb_item(Item_func::Functype func_type, uint no_args)
- : type(NDB_FUNCTION)
- {
- qualification.function_type= item_func_to_ndb_func(func_type);
- value.arg_count= no_args;
- };
- ~Ndb_item()
- {
- if (type == NDB_FIELD)
- {
- delete value.field_value;
- value.field_value= NULL;
- }
- };
-
- uint32 pack_length()
- {
- switch(type) {
- case(NDB_VALUE):
- if(qualification.value_type == Item::STRING_ITEM)
- return value.item->str_value.length();
- break;
- case(NDB_FIELD):
- return value.field_value->field->pack_length();
- default:
- break;
- }
-
- return 0;
- };
-
- Field * get_field() { return value.field_value->field; };
-
- int get_field_no() { return value.field_value->column_no; };
-
- int argument_count()
- {
- return value.arg_count;
- };
-
- const char* get_val()
- {
- switch(type) {
- case(NDB_VALUE):
- if(qualification.value_type == Item::STRING_ITEM)
- return value.item->str_value.ptr();
- break;
- case(NDB_FIELD):
- return value.field_value->field->ptr;
- default:
- break;
- }
-
- return NULL;
- };
-
- void save_in_field(Ndb_item *field_item)
- {
- Field *field = field_item->value.field_value->field;
- const Item *item= value.item;
-
- if (item && field)
- {
- my_bitmap_map *old_map=
- dbug_tmp_use_all_columns(field->table, field->table->write_set);
- ((Item *)item)->save_in_field(field, FALSE);
- dbug_tmp_restore_column_map(field->table->write_set, old_map);
- }
- };
-
- static NDB_FUNC_TYPE item_func_to_ndb_func(Item_func::Functype fun)
- {
- switch (fun) {
- case (Item_func::EQ_FUNC): { return NDB_EQ_FUNC; }
- case (Item_func::NE_FUNC): { return NDB_NE_FUNC; }
- case (Item_func::LT_FUNC): { return NDB_LT_FUNC; }
- case (Item_func::LE_FUNC): { return NDB_LE_FUNC; }
- case (Item_func::GT_FUNC): { return NDB_GT_FUNC; }
- case (Item_func::GE_FUNC): { return NDB_GE_FUNC; }
- case (Item_func::ISNULL_FUNC): { return NDB_ISNULL_FUNC; }
- case (Item_func::ISNOTNULL_FUNC): { return NDB_ISNOTNULL_FUNC; }
- case (Item_func::LIKE_FUNC): { return NDB_LIKE_FUNC; }
- case (Item_func::NOT_FUNC): { return NDB_NOT_FUNC; }
- case (Item_func::UNKNOWN_FUNC): { return NDB_UNKNOWN_FUNC; }
- case (Item_func::COND_AND_FUNC): { return NDB_COND_AND_FUNC; }
- case (Item_func::COND_OR_FUNC): { return NDB_COND_OR_FUNC; }
- default: { return NDB_UNSUPPORTED_FUNC; }
- }
- };
-
- static NDB_FUNC_TYPE negate(NDB_FUNC_TYPE fun)
- {
- uint i= (uint) fun;
- DBUG_ASSERT(fun == neg_map[i].pos_fun);
- return neg_map[i].neg_fun;
- };
-
- NDB_ITEM_TYPE type;
- NDB_ITEM_QUALIFICATION qualification;
- private:
- NDB_ITEM_VALUE value;
-};
-
-/*
- This class implements a linked list used for storing a
- serialization of the Item tree for condition pushdown.
- */
-class Ndb_cond
-{
- public:
- Ndb_cond() : ndb_item(NULL), next(NULL), prev(NULL) {};
- ~Ndb_cond()
- {
- if (ndb_item) delete ndb_item;
- ndb_item= NULL;
- if (next) delete next;
- next= prev= NULL;
- };
- Ndb_item *ndb_item;
- Ndb_cond *next;
- Ndb_cond *prev;
-};
-
-/*
- This class implements a stack for storing several conditions
- for pushdown (represented as serialized Item trees using Ndb_cond).
- The current implementation only pushes one condition, but is
- prepared for handling several (C1 AND C2 ...) if the logic for
- pushing conditions is extended in sql_select.
-*/
-class Ndb_cond_stack
-{
- public:
- Ndb_cond_stack() : ndb_cond(NULL), next(NULL) {};
- ~Ndb_cond_stack()
- {
- if (ndb_cond) delete ndb_cond;
- ndb_cond= NULL;
- if (next) delete next;
- next= NULL;
- };
- Ndb_cond *ndb_cond;
- Ndb_cond_stack *next;
-};
-
-class Ndb_rewrite_context
-{
-public:
- Ndb_rewrite_context(Item_func *func)
- : func_item(func), left_hand_item(NULL), count(0) {};
- ~Ndb_rewrite_context()
- {
- if (next) delete next;
- }
- const Item_func *func_item;
- const Item *left_hand_item;
- uint count;
- Ndb_rewrite_context *next;
-};
-
-/*
- This class is used for storing the context when traversing
- the Item tree. It stores a reference to the table the condition
- is defined on, the serialized representation being generated,
- if the condition found is supported, and information what is
- expected next in the tree inorder for the condition to be supported.
-*/
-class Ndb_cond_traverse_context
-{
- public:
- Ndb_cond_traverse_context(TABLE *tab, void* ndb_tab, Ndb_cond_stack* stack)
- : table(tab), ndb_table(ndb_tab),
- supported(TRUE), stack_ptr(stack), cond_ptr(NULL),
- skip(0), collation(NULL), rewrite_stack(NULL)
- {
- // Allocate type checking bitmaps
- bitmap_init(&expect_mask, 0, 512, FALSE);
- bitmap_init(&expect_field_type_mask, 0, 512, FALSE);
- bitmap_init(&expect_field_result_mask, 0, 512, FALSE);
-
- if (stack)
- cond_ptr= stack->ndb_cond;
- };
- ~Ndb_cond_traverse_context()
- {
- bitmap_free(&expect_mask);
- bitmap_free(&expect_field_type_mask);
- bitmap_free(&expect_field_result_mask);
- if (rewrite_stack) delete rewrite_stack;
- }
- void expect(Item::Type type)
- {
- bitmap_set_bit(&expect_mask, (uint) type);
- if (type == Item::FIELD_ITEM) expect_all_field_types();
- };
- void dont_expect(Item::Type type)
- {
- bitmap_clear_bit(&expect_mask, (uint) type);
- };
- bool expecting(Item::Type type)
- {
- return bitmap_is_set(&expect_mask, (uint) type);
- };
- void expect_nothing()
- {
- bitmap_clear_all(&expect_mask);
- };
- bool expecting_nothing()
- {
- return bitmap_is_clear_all(&expect_mask);
- }
- void expect_only(Item::Type type)
- {
- expect_nothing();
- expect(type);
- };
-
- void expect_field_type(enum_field_types type)
- {
- bitmap_set_bit(&expect_field_type_mask, (uint) type);
- };
- void expect_all_field_types()
- {
- bitmap_set_all(&expect_field_type_mask);
- };
- bool expecting_field_type(enum_field_types type)
- {
- return bitmap_is_set(&expect_field_type_mask, (uint) type);
- };
- void expect_no_field_type()
- {
- bitmap_clear_all(&expect_field_type_mask);
- };
- bool expecting_no_field_type()
- {
- return bitmap_is_clear_all(&expect_field_type_mask);
- }
- void expect_only_field_type(enum_field_types result)
- {
- expect_no_field_type();
- expect_field_type(result);
- };
-
- void expect_field_result(Item_result result)
- {
- bitmap_set_bit(&expect_field_result_mask, (uint) result);
- };
- bool expecting_field_result(Item_result result)
- {
- return bitmap_is_set(&expect_field_result_mask, (uint) result);
- };
- void expect_no_field_result()
- {
- bitmap_clear_all(&expect_field_result_mask);
- };
- bool expecting_no_field_result()
- {
- return bitmap_is_clear_all(&expect_field_result_mask);
- }
- void expect_only_field_result(Item_result result)
- {
- expect_no_field_result();
- expect_field_result(result);
- };
- void expect_collation(CHARSET_INFO* col)
- {
- collation= col;
- };
- bool expecting_collation(CHARSET_INFO* col)
- {
- bool matching= (!collation) ? true : (collation == col);
- collation= NULL;
-
- return matching;
- };
-
- TABLE* table;
- void* ndb_table;
- bool supported;
- Ndb_cond_stack* stack_ptr;
- Ndb_cond* cond_ptr;
- MY_BITMAP expect_mask;
- MY_BITMAP expect_field_type_mask;
- MY_BITMAP expect_field_result_mask;
- uint skip;
- CHARSET_INFO* collation;
- Ndb_rewrite_context *rewrite_stack;
-};
-
-
typedef enum ndb_query_state_bits {
NDB_QUERY_NORMAL = 0,
NDB_QUERY_MULTI_READ_RANGE = 1
@@ -906,27 +488,6 @@ private:
void release_completed_operations(NdbTransaction*, bool);
- /*
- Condition pushdown
- */
- void cond_clear();
- bool serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond);
- int build_scan_filter_predicate(Ndb_cond* &cond,
- NdbScanFilter* filter,
- bool negated= false);
- int build_scan_filter_group(Ndb_cond* &cond,
- NdbScanFilter* filter);
- int build_scan_filter(Ndb_cond* &cond, NdbScanFilter* filter);
- int generate_scan_filter(Ndb_cond_stack* cond_stack,
- NdbScanOperation* op);
- int generate_scan_filter_from_cond(Ndb_cond_stack* cond_stack,
- NdbScanFilter& filter);
- int generate_scan_filter_from_key(NdbScanOperation* op,
- const KEY* key_info,
- const byte *key,
- uint key_len,
- byte *buf);
-
friend int execute_commit(ha_ndbcluster*, NdbTransaction*);
friend int execute_no_commit_ignore_no_key(ha_ndbcluster*, NdbTransaction*);
friend int execute_no_commit(ha_ndbcluster*, NdbTransaction*, bool);
@@ -982,7 +543,7 @@ private:
ha_rows m_autoincrement_prefetch;
bool m_transaction_on;
- Ndb_cond_stack *m_cond_stack;
+ ha_ndbcluster_cond *m_cond;
bool m_disable_multi_read;
byte *m_multi_range_result_ptr;
KEY_MULTI_RANGE *m_multi_ranges;
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index c65c81c088c..25a9ccdcb87 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1021,6 +1021,7 @@ ndbcluster_update_slock(THD *thd,
const NDBTAB *ndbtab= ndbtab_g.get_table();
NdbTransaction *trans= 0;
int retries= 100;
+ int retry_sleep= 10; /* 10 milliseconds, transaction */
const NDBCOL *col[SCHEMA_SIZE];
unsigned sz[SCHEMA_SIZE];
@@ -1122,6 +1123,7 @@ ndbcluster_update_slock(THD *thd,
{
if (trans)
ndb->closeTransaction(trans);
+ my_sleep(retry_sleep);
continue; // retry
}
}
@@ -1333,6 +1335,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
const NDBTAB *ndbtab= ndbtab_g.get_table();
NdbTransaction *trans= 0;
int retries= 100;
+ int retry_sleep= 10; /* 10 milliseconds, transaction */
const NDBCOL *col[SCHEMA_SIZE];
unsigned sz[SCHEMA_SIZE];
@@ -1443,6 +1446,7 @@ err:
{
if (trans)
ndb->closeTransaction(trans);
+ my_sleep(retry_sleep);
continue; // retry
}
}
@@ -2841,6 +2845,11 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
TABLE *table= share->table;
int retries= 100;
+ /*
+ 100 milliseconds, temporary error on schema operation can
+ take some time to be resolved
+ */
+ int retry_sleep= 100;
while (1)
{
pthread_mutex_lock(&injector_mutex);
@@ -2969,7 +2978,10 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
ndb->dropEventOperation(op);
pthread_mutex_unlock(&injector_mutex);
if (retries)
+ {
+ my_sleep(retry_sleep);
continue;
+ }
DBUG_RETURN(-1);
}
pthread_mutex_unlock(&injector_mutex);
diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc
new file mode 100644
index 00000000000..267be17c330
--- /dev/null
+++ b/sql/ha_ndbcluster_cond.cc
@@ -0,0 +1,1426 @@
+/* Copyright (C) 2000-2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ This file defines the NDB Cluster handler engine_condition_pushdown
+*/
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
+#include <ndbapi/NdbApi.hpp>
+#include "ha_ndbcluster_cond.h"
+
+// Typedefs for long names
+typedef NdbDictionary::Column NDBCOL;
+typedef NdbDictionary::Table NDBTAB;
+
+/*
+ Serialize the item tree into a linked list represented by Ndb_cond
+ for fast generation of NbdScanFilter. Adds information such as
+ position of fields that is not directly available in the Item tree.
+ Also checks if condition is supported.
+*/
+void ndb_serialize_cond(const Item *item, void *arg)
+{
+ Ndb_cond_traverse_context *context= (Ndb_cond_traverse_context *) arg;
+ DBUG_ENTER("ndb_serialize_cond");
+
+ // Check if we are skipping arguments to a function to be evaluated
+ if (context->skip)
+ {
+ DBUG_PRINT("info", ("Skiping argument %d", context->skip));
+ context->skip--;
+ switch (item->type()) {
+ case Item::FUNC_ITEM:
+ {
+ Item_func *func_item= (Item_func *) item;
+ context->skip+= func_item->argument_count();
+ break;
+ }
+ case Item::INT_ITEM:
+ case Item::REAL_ITEM:
+ case Item::STRING_ITEM:
+ case Item::VARBIN_ITEM:
+ case Item::DECIMAL_ITEM:
+ break;
+ default:
+ context->supported= FALSE;
+ break;
+ }
+
+ DBUG_VOID_RETURN;
+ }
+
+ if (context->supported)
+ {
+ Ndb_rewrite_context *rewrite_context2= context->rewrite_stack;
+ const Item_func *rewrite_func_item;
+ // Check if we are rewriting some unsupported function call
+ if (rewrite_context2 &&
+ (rewrite_func_item= rewrite_context2->func_item) &&
+ rewrite_context2->count++ == 0)
+ {
+ switch (rewrite_func_item->functype()) {
+ case Item_func::BETWEEN:
+ /*
+ Rewrite
+ <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
+ to <field>|<const> > <const1>|<field1> AND
+ <field>|<const> < <const2>|<field2>
+ or actually in prefix format
+ BEGIN(AND) GT(<field>|<const>, <const1>|<field1>),
+ LT(<field>|<const>, <const2>|<field2>), END()
+ */
+ case Item_func::IN_FUNC:
+ {
+ /*
+ Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
+ to <field>|<const> = <const1>|<field1> OR
+ <field> = <const2>|<field2> ...
+ or actually in prefix format
+ BEGIN(OR) EQ(<field>|<const>, <const1><field1>),
+ EQ(<field>|<const>, <const2>|<field2>), ... END()
+ Each part of the disjunction is added for each call
+ to ndb_serialize_cond and end of rewrite statement
+ is wrapped in end of ndb_serialize_cond
+ */
+ if (context->expecting(item->type()))
+ {
+ // This is the <field>|<const> item, save it in the rewrite context
+ rewrite_context2->left_hand_item= item;
+ if (item->type() == Item::FUNC_ITEM)
+ {
+ Item_func *func_item= (Item_func *) item;
+ if (func_item->functype() == Item_func::UNKNOWN_FUNC &&
+ func_item->const_item())
+ {
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Found unsupported functional expression in BETWEEN|IN"));
+ context->supported= FALSE;
+ DBUG_VOID_RETURN;
+
+ }
+ }
+ }
+ else
+ {
+ // Non-supported BETWEEN|IN expression
+ DBUG_PRINT("info", ("Found unexpected item of type %u in BETWEEN|IN",
+ item->type()));
+ context->supported= FALSE;
+ DBUG_VOID_RETURN;
+ }
+ break;
+ }
+ default:
+ context->supported= FALSE;
+ break;
+ }
+ DBUG_VOID_RETURN;
+ }
+ else
+ {
+ Ndb_cond_stack *ndb_stack= context->stack_ptr;
+ Ndb_cond *prev_cond= context->cond_ptr;
+ Ndb_cond *curr_cond= context->cond_ptr= new Ndb_cond();
+ if (!ndb_stack->ndb_cond)
+ ndb_stack->ndb_cond= curr_cond;
+ curr_cond->prev= prev_cond;
+ if (prev_cond) prev_cond->next= curr_cond;
+ // Check if we are rewriting some unsupported function call
+ if (context->rewrite_stack)
+ {
+ Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
+ const Item_func *func_item= rewrite_context->func_item;
+ switch (func_item->functype()) {
+ case Item_func::BETWEEN:
+ {
+ /*
+ Rewrite
+ <field>|<const> BETWEEN <const1>|<field1> AND <const2>|<field2>
+ to <field>|<const> > <const1>|<field1> AND
+ <field>|<const> < <const2>|<field2>
+ or actually in prefix format
+ BEGIN(AND) GT(<field>|<const>, <const1>|<field1>),
+ LT(<field>|<const>, <const2>|<field2>), END()
+ */
+ if (rewrite_context->count == 2)
+ {
+ // Lower limit of BETWEEN
+ DBUG_PRINT("info", ("GE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::GE_FUNC, 2);
+ }
+ else if (rewrite_context->count == 3)
+ {
+ // Upper limit of BETWEEN
+ DBUG_PRINT("info", ("LE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::LE_FUNC, 2);
+ }
+ else
+ {
+ // Illegal BETWEEN expression
+ DBUG_PRINT("info", ("Illegal BETWEEN expression"));
+ context->supported= FALSE;
+ DBUG_VOID_RETURN;
+ }
+ break;
+ }
+ case Item_func::IN_FUNC:
+ {
+ /*
+ Rewrite <field>|<const> IN(<const1>|<field1>, <const2>|<field2>,..)
+ to <field>|<const> = <const1>|<field1> OR
+ <field> = <const2>|<field2> ...
+ or actually in prefix format
+ BEGIN(OR) EQ(<field>|<const>, <const1><field1>),
+ EQ(<field>|<const>, <const2>|<field2>), ... END()
+ Each part of the disjunction is added for each call
+ to ndb_serialize_cond and end of rewrite statement
+ is wrapped in end of ndb_serialize_cond
+ */
+ DBUG_PRINT("info", ("EQ_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::EQ_FUNC, 2);
+ break;
+ }
+ default:
+ context->supported= FALSE;
+ }
+ // Handle left hand <field>|<const>
+ context->rewrite_stack= NULL; // Disable rewrite mode
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FUNC_ITEM);
+ ndb_serialize_cond(rewrite_context->left_hand_item, arg);
+ context->skip= 0; // Any FUNC_ITEM expression has already been parsed
+ context->rewrite_stack= rewrite_context; // Enable rewrite mode
+ if (!context->supported)
+ DBUG_VOID_RETURN;
+
+ prev_cond= context->cond_ptr;
+ curr_cond= context->cond_ptr= new Ndb_cond();
+ prev_cond->next= curr_cond;
+ }
+
+ // Check for end of AND/OR expression
+ if (!item)
+ {
+ // End marker for condition group
+ DBUG_PRINT("info", ("End of condition group"));
+ curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
+ }
+ else
+ {
+ switch (item->type()) {
+ case Item::FIELD_ITEM:
+ {
+ Item_field *field_item= (Item_field *) item;
+ Field *field= field_item->field;
+ enum_field_types type= field->type();
+ /*
+ Check that the field is part of the table of the handler
+ instance and that we expect a field with of this result type.
+ */
+ if (context->table->s == field->table->s)
+ {
+ const NDBTAB *tab= context->ndb_table;
+ DBUG_PRINT("info", ("FIELD_ITEM"));
+ DBUG_PRINT("info", ("table %s", tab->getName()));
+ DBUG_PRINT("info", ("column %s", field->field_name));
+ DBUG_PRINT("info", ("type %d", field->type()));
+ DBUG_PRINT("info", ("result type %d", field->result_type()));
+
+ // Check that we are expecting a field and with the correct
+ // result type
+ if (context->expecting(Item::FIELD_ITEM) &&
+ context->expecting_field_type(field->type()) &&
+ (context->expecting_field_result(field->result_type()) ||
+ // Date and year can be written as string or int
+ ((type == MYSQL_TYPE_TIME ||
+ type == MYSQL_TYPE_DATE ||
+ type == MYSQL_TYPE_YEAR ||
+ type == MYSQL_TYPE_DATETIME)
+ ? (context->expecting_field_result(STRING_RESULT) ||
+ context->expecting_field_result(INT_RESULT))
+ : TRUE)) &&
+ // Bit fields no yet supported in scan filter
+ type != MYSQL_TYPE_BIT &&
+ // No BLOB support in scan filter
+ type != MYSQL_TYPE_TINY_BLOB &&
+ type != MYSQL_TYPE_MEDIUM_BLOB &&
+ type != MYSQL_TYPE_LONG_BLOB &&
+ type != MYSQL_TYPE_BLOB)
+ {
+ const NDBCOL *col= tab->getColumn(field->field_name);
+ DBUG_ASSERT(col);
+ curr_cond->ndb_item= new Ndb_item(field, col->getColumnNo());
+ context->dont_expect(Item::FIELD_ITEM);
+ context->expect_no_field_result();
+ if (! context->expecting_nothing())
+ {
+ // We have not seen second argument yet
+ if (type == MYSQL_TYPE_TIME ||
+ type == MYSQL_TYPE_DATE ||
+ type == MYSQL_TYPE_YEAR ||
+ type == MYSQL_TYPE_DATETIME)
+ {
+ context->expect_only(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ }
+ else
+ switch (field->result_type()) {
+ case STRING_RESULT:
+ // Expect char string or binary string
+ context->expect_only(Item::STRING_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect_collation(field_item->collation.collation);
+ break;
+ case REAL_RESULT:
+ context->expect_only(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::INT_ITEM);
+ break;
+ case INT_RESULT:
+ context->expect_only(Item::INT_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ break;
+ case DECIMAL_RESULT:
+ context->expect_only(Item::DECIMAL_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::INT_ITEM);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ // Check that field and string constant collations are the same
+ if ((field->result_type() == STRING_RESULT) &&
+ !context->expecting_collation(item->collation.collation)
+ && type != MYSQL_TYPE_TIME
+ && type != MYSQL_TYPE_DATE
+ && type != MYSQL_TYPE_YEAR
+ && type != MYSQL_TYPE_DATETIME)
+ {
+ DBUG_PRINT("info", ("Found non-matching collation %s",
+ item->collation.collation->name));
+ context->supported= FALSE;
+ }
+ }
+ break;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Was not expecting field of type %u(%u)",
+ field->result_type(), type));
+ context->supported= FALSE;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Was not expecting field from table %s (%s)",
+ context->table->s->table_name.str,
+ field->table->s->table_name.str));
+ context->supported= FALSE;
+ }
+ break;
+ }
+ case Item::FUNC_ITEM:
+ {
+ Item_func *func_item= (Item_func *) item;
+ // Check that we expect a function or functional expression here
+ if (context->expecting(Item::FUNC_ITEM) ||
+ func_item->functype() == Item_func::UNKNOWN_FUNC)
+ context->expect_nothing();
+ else
+ {
+ // Did not expect function here
+ context->supported= FALSE;
+ break;
+ }
+
+ switch (func_item->functype()) {
+ case Item_func::EQ_FUNC:
+ {
+ DBUG_PRINT("info", ("EQ_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::NE_FUNC:
+ {
+ DBUG_PRINT("info", ("NE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::LT_FUNC:
+ {
+ DBUG_PRINT("info", ("LT_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::LE_FUNC:
+ {
+ DBUG_PRINT("info", ("LE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::GE_FUNC:
+ {
+ DBUG_PRINT("info", ("GE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::GT_FUNC:
+ {
+ DBUG_PRINT("info", ("GT_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::REAL_ITEM);
+ context->expect(Item::DECIMAL_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::LIKE_FUNC:
+ {
+ DBUG_PRINT("info", ("LIKE_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_only_field_type(MYSQL_TYPE_STRING);
+ context->expect_field_type(MYSQL_TYPE_VAR_STRING);
+ context->expect_field_type(MYSQL_TYPE_VARCHAR);
+ context->expect_field_result(STRING_RESULT);
+ context->expect(Item::FUNC_ITEM);
+ break;
+ }
+ case Item_func::ISNULL_FUNC:
+ {
+ DBUG_PRINT("info", ("ISNULL_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::ISNOTNULL_FUNC:
+ {
+ DBUG_PRINT("info", ("ISNOTNULL_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::FIELD_ITEM);
+ context->expect_field_result(STRING_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(INT_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ break;
+ }
+ case Item_func::NOT_FUNC:
+ {
+ DBUG_PRINT("info", ("NOT_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(func_item->functype(),
+ func_item);
+ context->expect(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ break;
+ }
+ case Item_func::BETWEEN:
+ {
+ DBUG_PRINT("info", ("BETWEEN, rewriting using AND"));
+ Item_func_between *between_func= (Item_func_between *) func_item;
+ Ndb_rewrite_context *rewrite_context=
+ new Ndb_rewrite_context(func_item);
+ rewrite_context->next= context->rewrite_stack;
+ context->rewrite_stack= rewrite_context;
+ if (between_func->negated)
+ {
+ DBUG_PRINT("info", ("NOT_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
+ prev_cond= curr_cond;
+ curr_cond= context->cond_ptr= new Ndb_cond();
+ curr_cond->prev= prev_cond;
+ prev_cond->next= curr_cond;
+ }
+ DBUG_PRINT("info", ("COND_AND_FUNC"));
+ curr_cond->ndb_item=
+ new Ndb_item(Item_func::COND_AND_FUNC,
+ func_item->argument_count() - 1);
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FUNC_ITEM);
+ break;
+ }
+ case Item_func::IN_FUNC:
+ {
+ DBUG_PRINT("info", ("IN_FUNC, rewriting using OR"));
+ Item_func_in *in_func= (Item_func_in *) func_item;
+ Ndb_rewrite_context *rewrite_context=
+ new Ndb_rewrite_context(func_item);
+ rewrite_context->next= context->rewrite_stack;
+ context->rewrite_stack= rewrite_context;
+ if (in_func->negated)
+ {
+ DBUG_PRINT("info", ("NOT_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::NOT_FUNC, 1);
+ prev_cond= curr_cond;
+ curr_cond= context->cond_ptr= new Ndb_cond();
+ curr_cond->prev= prev_cond;
+ prev_cond->next= curr_cond;
+ }
+ DBUG_PRINT("info", ("COND_OR_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(Item_func::COND_OR_FUNC,
+ func_item->argument_count() - 1);
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect(Item::INT_ITEM);
+ context->expect(Item::STRING_ITEM);
+ context->expect(Item::VARBIN_ITEM);
+ context->expect(Item::FUNC_ITEM);
+ break;
+ }
+ case Item_func::UNKNOWN_FUNC:
+ {
+ DBUG_PRINT("info", ("UNKNOWN_FUNC %s",
+ func_item->const_item()?"const":""));
+ DBUG_PRINT("info", ("result type %d", func_item->result_type()));
+ if (func_item->const_item())
+ {
+ switch (func_item->result_type()) {
+ case STRING_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::STRING_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(STRING_RESULT);
+ context->expect_collation(func_item->collation.collation);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ // Check that string result have correct collation
+ if (!context->expecting_collation(item->collation.collation))
+ {
+ DBUG_PRINT("info", ("Found non-matching collation %s",
+ item->collation.collation->name));
+ context->supported= FALSE;
+ }
+ }
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
+ break;
+ }
+ case REAL_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::REAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(REAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
+ break;
+ }
+ case INT_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::INT_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(INT_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::DECIMAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(DECIMAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ // Skip any arguments since we will evaluate function instead
+ DBUG_PRINT("info", ("Skip until end of arguments marker"));
+ context->skip= func_item->argument_count();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ // Function does not return constant expression
+ context->supported= FALSE;
+ break;
+ }
+ default:
+ {
+ DBUG_PRINT("info", ("Found func_item of type %d",
+ func_item->functype()));
+ context->supported= FALSE;
+ }
+ }
+ break;
+ }
+ case Item::STRING_ITEM:
+ DBUG_PRINT("info", ("STRING_ITEM"));
+ if (context->expecting(Item::STRING_ITEM))
+ {
+#ifndef DBUG_OFF
+ char buff[256];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ str.length(0);
+ Item_string *string_item= (Item_string *) item;
+ DBUG_PRINT("info", ("value \"%s\"",
+ string_item->val_str(&str)->ptr()));
+#endif
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::STRING_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(STRING_RESULT);
+ context->expect_collation(item->collation.collation);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ // Check that we are comparing with a field with same collation
+ if (!context->expecting_collation(item->collation.collation))
+ {
+ DBUG_PRINT("info", ("Found non-matching collation %s",
+ item->collation.collation->name));
+ context->supported= FALSE;
+ }
+ }
+ }
+ else
+ context->supported= FALSE;
+ break;
+ case Item::INT_ITEM:
+ DBUG_PRINT("info", ("INT_ITEM"));
+ if (context->expecting(Item::INT_ITEM))
+ {
+ DBUG_PRINT("info", ("value %ld",
+ (long) ((Item_int*) item)->value));
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::INT_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(INT_RESULT);
+ context->expect_field_result(REAL_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ }
+ else
+ context->supported= FALSE;
+ break;
+ case Item::REAL_ITEM:
+ DBUG_PRINT("info", ("REAL_ITEM"));
+ if (context->expecting(Item::REAL_ITEM))
+ {
+ DBUG_PRINT("info", ("value %f", ((Item_float*) item)->value));
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::REAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(REAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ }
+ else
+ context->supported= FALSE;
+ break;
+ case Item::VARBIN_ITEM:
+ DBUG_PRINT("info", ("VARBIN_ITEM"));
+ if (context->expecting(Item::VARBIN_ITEM))
+ {
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::VARBIN_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(STRING_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ }
+ else
+ context->supported= FALSE;
+ break;
+ case Item::DECIMAL_ITEM:
+ DBUG_PRINT("info", ("DECIMAL_ITEM"));
+ if (context->expecting(Item::DECIMAL_ITEM))
+ {
+ DBUG_PRINT("info", ("value %f",
+ ((Item_decimal*) item)->val_real()));
+ NDB_ITEM_QUALIFICATION q;
+ q.value_type= Item::DECIMAL_ITEM;
+ curr_cond->ndb_item= new Ndb_item(NDB_VALUE, q, item);
+ if (! context->expecting_no_field_result())
+ {
+ // We have not seen the field argument yet
+ context->expect_only(Item::FIELD_ITEM);
+ context->expect_only_field_result(REAL_RESULT);
+ context->expect_field_result(DECIMAL_RESULT);
+ }
+ else
+ {
+ // Expect another logical expression
+ context->expect_only(Item::FUNC_ITEM);
+ context->expect(Item::COND_ITEM);
+ }
+ }
+ else
+ context->supported= FALSE;
+ break;
+ case Item::COND_ITEM:
+ {
+ Item_cond *cond_item= (Item_cond *) item;
+
+ if (context->expecting(Item::COND_ITEM))
+ {
+ switch (cond_item->functype()) {
+ case Item_func::COND_AND_FUNC:
+ DBUG_PRINT("info", ("COND_AND_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
+ cond_item);
+ break;
+ case Item_func::COND_OR_FUNC:
+ DBUG_PRINT("info", ("COND_OR_FUNC"));
+ curr_cond->ndb_item= new Ndb_item(cond_item->functype(),
+ cond_item);
+ break;
+ default:
+ DBUG_PRINT("info", ("COND_ITEM %d", cond_item->functype()));
+ context->supported= FALSE;
+ break;
+ }
+ }
+ else
+ {
+ /* Did not expect condition */
+ context->supported= FALSE;
+ }
+ break;
+ }
+ default:
+ {
+ DBUG_PRINT("info", ("Found item of type %d", item->type()));
+ context->supported= FALSE;
+ }
+ }
+ }
+ if (context->supported && context->rewrite_stack)
+ {
+ Ndb_rewrite_context *rewrite_context= context->rewrite_stack;
+ if (rewrite_context->count ==
+ rewrite_context->func_item->argument_count())
+ {
+ // Rewrite is done, wrap an END() at the en
+ DBUG_PRINT("info", ("End of condition group"));
+ prev_cond= curr_cond;
+ curr_cond= context->cond_ptr= new Ndb_cond();
+ curr_cond->prev= prev_cond;
+ prev_cond->next= curr_cond;
+ curr_cond->ndb_item= new Ndb_item(NDB_END_COND);
+ // Pop rewrite stack
+ context->rewrite_stack= rewrite_context->next;
+ rewrite_context->next= NULL;
+ delete(rewrite_context);
+ }
+ }
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Push a condition
+ */
+const
+COND*
+ha_ndbcluster_cond::cond_push(const COND *cond,
+ TABLE *table, const NDBTAB *ndb_table)
+{
+ DBUG_ENTER("cond_push");
+ Ndb_cond_stack *ndb_cond = new Ndb_cond_stack();
+ if (ndb_cond == NULL)
+ {
+ my_errno= HA_ERR_OUT_OF_MEM;
+ DBUG_RETURN(NULL);
+ }
+ if (m_cond_stack)
+ ndb_cond->next= m_cond_stack;
+ else
+ ndb_cond->next= NULL;
+ m_cond_stack= ndb_cond;
+
+ if (serialize_cond(cond, ndb_cond, table, ndb_table))
+ {
+ DBUG_RETURN(NULL);
+ }
+ else
+ {
+ cond_pop();
+ }
+ DBUG_RETURN(cond);
+}
+
+/*
+ Pop the top condition from the condition stack
+*/
+void
+ha_ndbcluster_cond::cond_pop()
+{
+ Ndb_cond_stack *ndb_cond_stack= m_cond_stack;
+ if (ndb_cond_stack)
+ {
+ m_cond_stack= ndb_cond_stack->next;
+ ndb_cond_stack->next= NULL;
+ delete ndb_cond_stack;
+ }
+}
+
+/*
+ Clear the condition stack
+*/
+void
+ha_ndbcluster_cond::cond_clear()
+{
+ DBUG_ENTER("cond_clear");
+ while (m_cond_stack)
+ cond_pop();
+
+ DBUG_VOID_RETURN;
+}
+
+bool
+ha_ndbcluster_cond::serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond,
+ TABLE *table, const NDBTAB *ndb_table)
+{
+ DBUG_ENTER("serialize_cond");
+ Item *item= (Item *) cond;
+ Ndb_cond_traverse_context context(table, ndb_table, ndb_cond);
+ // Expect a logical expression
+ context.expect(Item::FUNC_ITEM);
+ context.expect(Item::COND_ITEM);
+ item->traverse_cond(&ndb_serialize_cond, (void *) &context, Item::PREFIX);
+ DBUG_PRINT("info", ("The pushed condition is %ssupported", (context.supported)?"":"not "));
+
+ DBUG_RETURN(context.supported);
+}
+
+int
+ha_ndbcluster_cond::build_scan_filter_predicate(Ndb_cond * &cond,
+ NdbScanFilter *filter,
+ bool negated)
+{
+ DBUG_ENTER("build_scan_filter_predicate");
+ switch (cond->ndb_item->type) {
+ case NDB_FUNCTION:
+ {
+ if (!cond->next)
+ break;
+ Ndb_item *a= cond->next->ndb_item;
+ Ndb_item *b, *field, *value= NULL;
+
+ switch (cond->ndb_item->argument_count()) {
+ case 1:
+ field= (a->type == NDB_FIELD)? a : NULL;
+ break;
+ case 2:
+ if (!cond->next->next)
+ {
+ field= NULL;
+ break;
+ }
+ b= cond->next->next->ndb_item;
+ value= ((a->type == NDB_VALUE) ? a :
+ (b->type == NDB_VALUE) ? b :
+ NULL);
+ field= ((a->type == NDB_FIELD) ? a :
+ (b->type == NDB_FIELD) ? b :
+ NULL);
+ break;
+ default:
+ field= NULL; //Keep compiler happy
+ DBUG_ASSERT(0);
+ break;
+ }
+ switch ((negated) ?
+ Ndb_item::negate(cond->ndb_item->qualification.function_type)
+ : cond->ndb_item->qualification.function_type) {
+ case NDB_EQ_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ DBUG_PRINT("info", ("Generating EQ filter"));
+ if (filter->cmp(NdbScanFilter::COND_EQ,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_NE_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ DBUG_PRINT("info", ("Generating NE filter"));
+ if (filter->cmp(NdbScanFilter::COND_NE,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_LT_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ if (a == field)
+ {
+ DBUG_PRINT("info", ("Generating LT filter"));
+ if (filter->cmp(NdbScanFilter::COND_LT,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Generating GT filter"));
+ if (filter->cmp(NdbScanFilter::COND_GT,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_LE_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ if (a == field)
+ {
+ DBUG_PRINT("info", ("Generating LE filter"));
+ if (filter->cmp(NdbScanFilter::COND_LE,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Generating GE filter"));
+ if (filter->cmp(NdbScanFilter::COND_GE,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_GE_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ if (a == field)
+ {
+ DBUG_PRINT("info", ("Generating GE filter"));
+ if (filter->cmp(NdbScanFilter::COND_GE,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Generating LE filter"));
+ if (filter->cmp(NdbScanFilter::COND_LE,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_GT_FUNC:
+ {
+ if (!value || !field) break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ if (a == field)
+ {
+ DBUG_PRINT("info", ("Generating GT filter"));
+ if (filter->cmp(NdbScanFilter::COND_GT,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Generating LT filter"));
+ if (filter->cmp(NdbScanFilter::COND_LT,
+ field->get_field_no(),
+ field->get_val(),
+ field->pack_length()) == -1)
+ DBUG_RETURN(1);
+ }
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_LIKE_FUNC:
+ {
+ if (!value || !field) break;
+ if ((value->qualification.value_type != Item::STRING_ITEM) &&
+ (value->qualification.value_type != Item::VARBIN_ITEM))
+ break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ DBUG_PRINT("info", ("Generating LIKE filter: like(%d,%s,%d)",
+ field->get_field_no(), value->get_val(),
+ value->pack_length()));
+ if (filter->cmp(NdbScanFilter::COND_LIKE,
+ field->get_field_no(),
+ value->get_val(),
+ value->pack_length()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_NOTLIKE_FUNC:
+ {
+ if (!value || !field) break;
+ if ((value->qualification.value_type != Item::STRING_ITEM) &&
+ (value->qualification.value_type != Item::VARBIN_ITEM))
+ break;
+ // Save value in right format for the field type
+ value->save_in_field(field);
+ DBUG_PRINT("info", ("Generating NOTLIKE filter: notlike(%d,%s,%d)",
+ field->get_field_no(), value->get_val(),
+ value->pack_length()));
+ if (filter->cmp(NdbScanFilter::COND_NOT_LIKE,
+ field->get_field_no(),
+ value->get_val(),
+ value->pack_length()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next->next;
+ DBUG_RETURN(0);
+ }
+ case NDB_ISNULL_FUNC:
+ if (!field)
+ break;
+ DBUG_PRINT("info", ("Generating ISNULL filter"));
+ if (filter->isnull(field->get_field_no()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next;
+ DBUG_RETURN(0);
+ case NDB_ISNOTNULL_FUNC:
+ {
+ if (!field)
+ break;
+ DBUG_PRINT("info", ("Generating ISNOTNULL filter"));
+ if (filter->isnotnull(field->get_field_no()) == -1)
+ DBUG_RETURN(1);
+ cond= cond->next->next;
+ DBUG_RETURN(0);
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ DBUG_PRINT("info", ("Found illegal condition"));
+ DBUG_RETURN(1);
+}
+
+
+int
+ha_ndbcluster_cond::build_scan_filter_group(Ndb_cond* &cond,
+ NdbScanFilter *filter)
+{
+ uint level=0;
+ bool negated= FALSE;
+ DBUG_ENTER("build_scan_filter_group");
+
+ do
+ {
+ if (!cond)
+ DBUG_RETURN(1);
+ switch (cond->ndb_item->type) {
+ case NDB_FUNCTION:
+ {
+ switch (cond->ndb_item->qualification.function_type) {
+ case NDB_COND_AND_FUNC:
+ {
+ level++;
+ DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NAND":"AND",
+ level));
+ if ((negated) ? filter->begin(NdbScanFilter::NAND)
+ : filter->begin(NdbScanFilter::AND) == -1)
+ DBUG_RETURN(1);
+ negated= FALSE;
+ cond= cond->next;
+ break;
+ }
+ case NDB_COND_OR_FUNC:
+ {
+ level++;
+ DBUG_PRINT("info", ("Generating %s group %u", (negated)?"NOR":"OR",
+ level));
+ if ((negated) ? filter->begin(NdbScanFilter::NOR)
+ : filter->begin(NdbScanFilter::OR) == -1)
+ DBUG_RETURN(1);
+ negated= FALSE;
+ cond= cond->next;
+ break;
+ }
+ case NDB_NOT_FUNC:
+ {
+ DBUG_PRINT("info", ("Generating negated query"));
+ cond= cond->next;
+ negated= TRUE;
+ break;
+ }
+ default:
+ if (build_scan_filter_predicate(cond, filter, negated))
+ DBUG_RETURN(1);
+ negated= FALSE;
+ break;
+ }
+ break;
+ }
+ case NDB_END_COND:
+ DBUG_PRINT("info", ("End of group %u", level));
+ level--;
+ if (cond) cond= cond->next;
+ if (filter->end() == -1)
+ DBUG_RETURN(1);
+ if (!negated)
+ break;
+ // else fall through (NOT END is an illegal condition)
+ default:
+ {
+ DBUG_PRINT("info", ("Illegal scan filter"));
+ }
+ }
+ } while (level > 0 || negated);
+
+ DBUG_RETURN(0);
+}
+
+
+int
+ha_ndbcluster_cond::build_scan_filter(Ndb_cond * &cond, NdbScanFilter *filter)
+{
+ bool simple_cond= TRUE;
+ DBUG_ENTER("build_scan_filter");
+
+ switch (cond->ndb_item->type) {
+ case NDB_FUNCTION:
+ switch (cond->ndb_item->qualification.function_type) {
+ case NDB_COND_AND_FUNC:
+ case NDB_COND_OR_FUNC:
+ simple_cond= FALSE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (simple_cond && filter->begin() == -1)
+ DBUG_RETURN(1);
+ if (build_scan_filter_group(cond, filter))
+ DBUG_RETURN(1);
+ if (simple_cond && filter->end() == -1)
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
+int
+ha_ndbcluster_cond::generate_scan_filter(NdbScanOperation *op)
+{
+ DBUG_ENTER("generate_scan_filter");
+
+ if (m_cond_stack)
+ {
+ NdbScanFilter filter(op);
+
+ DBUG_RETURN(generate_scan_filter_from_cond(filter));
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Empty stack"));
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+int
+ha_ndbcluster_cond::generate_scan_filter_from_cond(NdbScanFilter& filter)
+{
+ bool multiple_cond= FALSE;
+ DBUG_ENTER("generate_scan_filter_from_cond");
+
+ // Wrap an AND group around multiple conditions
+ if (m_cond_stack->next)
+ {
+ multiple_cond= TRUE;
+ if (filter.begin() == -1)
+ DBUG_RETURN(1);
+ }
+ for (Ndb_cond_stack *stack= m_cond_stack;
+ (stack);
+ stack= stack->next)
+ {
+ Ndb_cond *cond= stack->ndb_cond;
+
+ if (build_scan_filter(cond, &filter))
+ {
+ DBUG_PRINT("info", ("build_scan_filter failed"));
+ DBUG_RETURN(1);
+ }
+ }
+ if (multiple_cond && filter.end() == -1)
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
+
+int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op,
+ const KEY* key_info,
+ const byte *key,
+ uint key_len,
+ byte *buf)
+{
+ KEY_PART_INFO* key_part= key_info->key_part;
+ KEY_PART_INFO* end= key_part+key_info->key_parts;
+ NdbScanFilter filter(op);
+ int res;
+ DBUG_ENTER("generate_scan_filter_from_key");
+
+ filter.begin(NdbScanFilter::AND);
+ for (; key_part != end; key_part++)
+ {
+ Field* field= key_part->field;
+ uint32 pack_len= field->pack_length();
+ const byte* ptr= key;
+ DBUG_PRINT("info", ("Filtering value for %s", field->field_name));
+ DBUG_DUMP("key", (char*)ptr, pack_len);
+ if (key_part->null_bit)
+ {
+ DBUG_PRINT("info", ("Generating ISNULL filter"));
+ if (filter.isnull(key_part->fieldnr-1) == -1)
+ DBUG_RETURN(1);
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Generating EQ filter"));
+ if (filter.cmp(NdbScanFilter::COND_EQ,
+ key_part->fieldnr-1,
+ ptr,
+ pack_len) == -1)
+ DBUG_RETURN(1);
+ }
+ key += key_part->store_length;
+ }
+ // Add any pushed condition
+ if (m_cond_stack &&
+ (res= generate_scan_filter_from_cond(filter)))
+ DBUG_RETURN(res);
+
+ if (filter.end() == -1)
+ DBUG_RETURN(1);
+
+ DBUG_RETURN(0);
+}
+
+#endif /* HAVE_NDBCLUSTER_DB */
diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h
new file mode 100644
index 00000000000..617302107d8
--- /dev/null
+++ b/sql/ha_ndbcluster_cond.h
@@ -0,0 +1,475 @@
+/* Copyright (C) 2000-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ This file defines the data structures used by engine condition pushdown in
+ the NDB Cluster handler
+*/
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class implementation */
+#endif
+
+typedef enum ndb_item_type {
+ NDB_VALUE = 0, // Qualified more with Item::Type
+ NDB_FIELD = 1, // Qualified from table definition
+ NDB_FUNCTION = 2,// Qualified from Item_func::Functype
+ NDB_END_COND = 3 // End marker for condition group
+} NDB_ITEM_TYPE;
+
+typedef enum ndb_func_type {
+ NDB_EQ_FUNC = 0,
+ NDB_NE_FUNC = 1,
+ NDB_LT_FUNC = 2,
+ NDB_LE_FUNC = 3,
+ NDB_GT_FUNC = 4,
+ NDB_GE_FUNC = 5,
+ NDB_ISNULL_FUNC = 6,
+ NDB_ISNOTNULL_FUNC = 7,
+ NDB_LIKE_FUNC = 8,
+ NDB_NOTLIKE_FUNC = 9,
+ NDB_NOT_FUNC = 10,
+ NDB_UNKNOWN_FUNC = 11,
+ NDB_COND_AND_FUNC = 12,
+ NDB_COND_OR_FUNC = 13,
+ NDB_UNSUPPORTED_FUNC = 14
+} NDB_FUNC_TYPE;
+
+typedef union ndb_item_qualification {
+ Item::Type value_type;
+ enum_field_types field_type; // Instead of Item::FIELD_ITEM
+ NDB_FUNC_TYPE function_type; // Instead of Item::FUNC_ITEM
+} NDB_ITEM_QUALIFICATION;
+
+typedef struct ndb_item_field_value {
+ Field* field;
+ int column_no;
+} NDB_ITEM_FIELD_VALUE;
+
+typedef union ndb_item_value {
+ const Item *item;
+ NDB_ITEM_FIELD_VALUE *field_value;
+ uint arg_count;
+} NDB_ITEM_VALUE;
+
+struct negated_function_mapping
+{
+ NDB_FUNC_TYPE pos_fun;
+ NDB_FUNC_TYPE neg_fun;
+};
+
+/*
+ Define what functions can be negated in condition pushdown.
+ Note, these HAVE to be in the same order as in definition enum
+*/
+static const negated_function_mapping neg_map[]=
+{
+ {NDB_EQ_FUNC, NDB_NE_FUNC},
+ {NDB_NE_FUNC, NDB_EQ_FUNC},
+ {NDB_LT_FUNC, NDB_GE_FUNC},
+ {NDB_LE_FUNC, NDB_GT_FUNC},
+ {NDB_GT_FUNC, NDB_LE_FUNC},
+ {NDB_GE_FUNC, NDB_LT_FUNC},
+ {NDB_ISNULL_FUNC, NDB_ISNOTNULL_FUNC},
+ {NDB_ISNOTNULL_FUNC, NDB_ISNULL_FUNC},
+ {NDB_LIKE_FUNC, NDB_NOTLIKE_FUNC},
+ {NDB_NOTLIKE_FUNC, NDB_LIKE_FUNC},
+ {NDB_NOT_FUNC, NDB_UNSUPPORTED_FUNC},
+ {NDB_UNKNOWN_FUNC, NDB_UNSUPPORTED_FUNC},
+ {NDB_COND_AND_FUNC, NDB_UNSUPPORTED_FUNC},
+ {NDB_COND_OR_FUNC, NDB_UNSUPPORTED_FUNC},
+ {NDB_UNSUPPORTED_FUNC, NDB_UNSUPPORTED_FUNC}
+};
+
+/*
+ This class is the construction element for serialization of Item tree
+ in condition pushdown.
+ An instance of Ndb_Item represents a constant, table field reference,
+ unary or binary comparison predicate, and start/end of AND/OR.
+ Instances of Ndb_Item are stored in a linked list implemented by Ndb_cond
+ class.
+ The order of elements produced by Ndb_cond::next corresponds to
+ breadth-first traversal of the Item (i.e. expression) tree in prefix order.
+ AND and OR have arbitrary arity, so the end of AND/OR group is marked with
+ Ndb_item with type == NDB_END_COND.
+ NOT items represent negated conditions and generate NAND/NOR groups.
+*/
+class Ndb_item : public Sql_alloc
+{
+public:
+ Ndb_item(NDB_ITEM_TYPE item_type) : type(item_type) {};
+ Ndb_item(NDB_ITEM_TYPE item_type,
+ NDB_ITEM_QUALIFICATION item_qualification,
+ const Item *item_value)
+ : type(item_type), qualification(item_qualification)
+ {
+ switch(item_type) {
+ case(NDB_VALUE):
+ value.item= item_value;
+ break;
+ case(NDB_FIELD): {
+ NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE();
+ Item_field *field_item= (Item_field *) item_value;
+ field_value->field= field_item->field;
+ field_value->column_no= -1; // Will be fetched at scan filter generation
+ value.field_value= field_value;
+ break;
+ }
+ case(NDB_FUNCTION):
+ value.item= item_value;
+ value.arg_count= ((Item_func *) item_value)->argument_count();
+ break;
+ case(NDB_END_COND):
+ break;
+ }
+ };
+ Ndb_item(Field *field, int column_no) : type(NDB_FIELD)
+ {
+ NDB_ITEM_FIELD_VALUE *field_value= new NDB_ITEM_FIELD_VALUE();
+ qualification.field_type= field->type();
+ field_value->field= field;
+ field_value->column_no= column_no;
+ value.field_value= field_value;
+ };
+ Ndb_item(Item_func::Functype func_type, const Item *item_value)
+ : type(NDB_FUNCTION)
+ {
+ qualification.function_type= item_func_to_ndb_func(func_type);
+ value.item= item_value;
+ value.arg_count= ((Item_func *) item_value)->argument_count();
+ };
+ Ndb_item(Item_func::Functype func_type, uint no_args)
+ : type(NDB_FUNCTION)
+ {
+ qualification.function_type= item_func_to_ndb_func(func_type);
+ value.arg_count= no_args;
+ };
+ ~Ndb_item()
+ {
+ if (type == NDB_FIELD)
+ {
+ delete value.field_value;
+ value.field_value= NULL;
+ }
+ };
+
+ uint32 pack_length()
+ {
+ switch(type) {
+ case(NDB_VALUE):
+ if(qualification.value_type == Item::STRING_ITEM)
+ return value.item->str_value.length();
+ break;
+ case(NDB_FIELD):
+ return value.field_value->field->pack_length();
+ default:
+ break;
+ }
+
+ return 0;
+ };
+
+ Field * get_field() { return value.field_value->field; };
+
+ int get_field_no() { return value.field_value->column_no; };
+
+ int argument_count()
+ {
+ return value.arg_count;
+ };
+
+ const char* get_val()
+ {
+ switch(type) {
+ case(NDB_VALUE):
+ if(qualification.value_type == Item::STRING_ITEM)
+ return value.item->str_value.ptr();
+ break;
+ case(NDB_FIELD):
+ return value.field_value->field->ptr;
+ default:
+ break;
+ }
+
+ return NULL;
+ };
+
+ void save_in_field(Ndb_item *field_item)
+ {
+ Field *field = field_item->value.field_value->field;
+ const Item *item= value.item;
+
+ if (item && field)
+ {
+ my_bitmap_map *old_map=
+ dbug_tmp_use_all_columns(field->table, field->table->write_set);
+ ((Item *)item)->save_in_field(field, FALSE);
+ dbug_tmp_restore_column_map(field->table->write_set, old_map);
+ }
+ };
+
+ static NDB_FUNC_TYPE item_func_to_ndb_func(Item_func::Functype fun)
+ {
+ switch (fun) {
+ case (Item_func::EQ_FUNC): { return NDB_EQ_FUNC; }
+ case (Item_func::NE_FUNC): { return NDB_NE_FUNC; }
+ case (Item_func::LT_FUNC): { return NDB_LT_FUNC; }
+ case (Item_func::LE_FUNC): { return NDB_LE_FUNC; }
+ case (Item_func::GT_FUNC): { return NDB_GT_FUNC; }
+ case (Item_func::GE_FUNC): { return NDB_GE_FUNC; }
+ case (Item_func::ISNULL_FUNC): { return NDB_ISNULL_FUNC; }
+ case (Item_func::ISNOTNULL_FUNC): { return NDB_ISNOTNULL_FUNC; }
+ case (Item_func::LIKE_FUNC): { return NDB_LIKE_FUNC; }
+ case (Item_func::NOT_FUNC): { return NDB_NOT_FUNC; }
+ case (Item_func::UNKNOWN_FUNC): { return NDB_UNKNOWN_FUNC; }
+ case (Item_func::COND_AND_FUNC): { return NDB_COND_AND_FUNC; }
+ case (Item_func::COND_OR_FUNC): { return NDB_COND_OR_FUNC; }
+ default: { return NDB_UNSUPPORTED_FUNC; }
+ }
+ };
+
+ static NDB_FUNC_TYPE negate(NDB_FUNC_TYPE fun)
+ {
+ uint i= (uint) fun;
+ DBUG_ASSERT(fun == neg_map[i].pos_fun);
+ return neg_map[i].neg_fun;
+ };
+
+ NDB_ITEM_TYPE type;
+ NDB_ITEM_QUALIFICATION qualification;
+ private:
+ NDB_ITEM_VALUE value;
+};
+
+/*
+ This class implements a linked list used for storing a
+ serialization of the Item tree for condition pushdown.
+ */
+class Ndb_cond : public Sql_alloc
+{
+ public:
+ Ndb_cond() : ndb_item(NULL), next(NULL), prev(NULL) {};
+ ~Ndb_cond()
+ {
+ if (ndb_item) delete ndb_item;
+ ndb_item= NULL;
+ if (next) delete next;
+ next= prev= NULL;
+ };
+ Ndb_item *ndb_item;
+ Ndb_cond *next;
+ Ndb_cond *prev;
+};
+
+/*
+ This class implements a stack for storing several conditions
+ for pushdown (represented as serialized Item trees using Ndb_cond).
+ The current implementation only pushes one condition, but is
+ prepared for handling several (C1 AND C2 ...) if the logic for
+ pushing conditions is extended in sql_select.
+*/
+class Ndb_cond_stack : public Sql_alloc
+{
+ public:
+ Ndb_cond_stack() : ndb_cond(NULL), next(NULL) {};
+ ~Ndb_cond_stack()
+ {
+ if (ndb_cond) delete ndb_cond;
+ ndb_cond= NULL;
+ if (next) delete next;
+ next= NULL;
+ };
+ Ndb_cond *ndb_cond;
+ Ndb_cond_stack *next;
+};
+
+class Ndb_rewrite_context : public Sql_alloc
+{
+public:
+ Ndb_rewrite_context(Item_func *func)
+ : func_item(func), left_hand_item(NULL), count(0) {};
+ ~Ndb_rewrite_context()
+ {
+ if (next) delete next;
+ }
+ const Item_func *func_item;
+ const Item *left_hand_item;
+ uint count;
+ Ndb_rewrite_context *next;
+};
+
+/*
+ This class is used for storing the context when traversing
+ the Item tree. It stores a reference to the table the condition
+ is defined on, the serialized representation being generated,
+ if the condition found is supported, and information what is
+ expected next in the tree inorder for the condition to be supported.
+*/
+class Ndb_cond_traverse_context : public Sql_alloc
+{
+ public:
+ Ndb_cond_traverse_context(TABLE *tab, const NdbDictionary::Table *ndb_tab,
+ Ndb_cond_stack* stack)
+ : table(tab), ndb_table(ndb_tab),
+ supported(TRUE), stack_ptr(stack), cond_ptr(NULL),
+ skip(0), collation(NULL), rewrite_stack(NULL)
+ {
+ // Allocate type checking bitmaps
+ bitmap_init(&expect_mask, 0, 512, FALSE);
+ bitmap_init(&expect_field_type_mask, 0, 512, FALSE);
+ bitmap_init(&expect_field_result_mask, 0, 512, FALSE);
+
+ if (stack)
+ cond_ptr= stack->ndb_cond;
+ };
+ ~Ndb_cond_traverse_context()
+ {
+ bitmap_free(&expect_mask);
+ bitmap_free(&expect_field_type_mask);
+ bitmap_free(&expect_field_result_mask);
+ if (rewrite_stack) delete rewrite_stack;
+ }
+ void expect(Item::Type type)
+ {
+ bitmap_set_bit(&expect_mask, (uint) type);
+ if (type == Item::FIELD_ITEM) expect_all_field_types();
+ };
+ void dont_expect(Item::Type type)
+ {
+ bitmap_clear_bit(&expect_mask, (uint) type);
+ };
+ bool expecting(Item::Type type)
+ {
+ return bitmap_is_set(&expect_mask, (uint) type);
+ };
+ void expect_nothing()
+ {
+ bitmap_clear_all(&expect_mask);
+ };
+ bool expecting_nothing()
+ {
+ return bitmap_is_clear_all(&expect_mask);
+ }
+ void expect_only(Item::Type type)
+ {
+ expect_nothing();
+ expect(type);
+ };
+
+ void expect_field_type(enum_field_types type)
+ {
+ bitmap_set_bit(&expect_field_type_mask, (uint) type);
+ };
+ void expect_all_field_types()
+ {
+ bitmap_set_all(&expect_field_type_mask);
+ };
+ bool expecting_field_type(enum_field_types type)
+ {
+ return bitmap_is_set(&expect_field_type_mask, (uint) type);
+ };
+ void expect_no_field_type()
+ {
+ bitmap_clear_all(&expect_field_type_mask);
+ };
+ bool expecting_no_field_type()
+ {
+ return bitmap_is_clear_all(&expect_field_type_mask);
+ }
+ void expect_only_field_type(enum_field_types result)
+ {
+ expect_no_field_type();
+ expect_field_type(result);
+ };
+
+ void expect_field_result(Item_result result)
+ {
+ bitmap_set_bit(&expect_field_result_mask, (uint) result);
+ };
+ bool expecting_field_result(Item_result result)
+ {
+ return bitmap_is_set(&expect_field_result_mask, (uint) result);
+ };
+ void expect_no_field_result()
+ {
+ bitmap_clear_all(&expect_field_result_mask);
+ };
+ bool expecting_no_field_result()
+ {
+ return bitmap_is_clear_all(&expect_field_result_mask);
+ }
+ void expect_only_field_result(Item_result result)
+ {
+ expect_no_field_result();
+ expect_field_result(result);
+ };
+ void expect_collation(CHARSET_INFO* col)
+ {
+ collation= col;
+ };
+ bool expecting_collation(CHARSET_INFO* col)
+ {
+ bool matching= (!collation) ? true : (collation == col);
+ collation= NULL;
+
+ return matching;
+ };
+
+ TABLE* table;
+ const NdbDictionary::Table *ndb_table;
+ bool supported;
+ Ndb_cond_stack* stack_ptr;
+ Ndb_cond* cond_ptr;
+ MY_BITMAP expect_mask;
+ MY_BITMAP expect_field_type_mask;
+ MY_BITMAP expect_field_result_mask;
+ uint skip;
+ CHARSET_INFO* collation;
+ Ndb_rewrite_context *rewrite_stack;
+};
+
+class ha_ndbcluster;
+
+class ha_ndbcluster_cond
+{
+public:
+ ha_ndbcluster_cond()
+ : m_cond_stack(NULL)
+ {}
+ ~ha_ndbcluster_cond()
+ { if (m_cond_stack) delete m_cond_stack; }
+ const COND *cond_push(const COND *cond,
+ TABLE *table, const NdbDictionary::Table *ndb_table);
+ void cond_pop();
+ void cond_clear();
+ int generate_scan_filter(NdbScanOperation* op);
+ int generate_scan_filter_from_cond(NdbScanFilter& filter);
+ int generate_scan_filter_from_key(NdbScanOperation* op,
+ const KEY* key_info,
+ const byte *key,
+ uint key_len,
+ byte *buf);
+private:
+ bool serialize_cond(const COND *cond, Ndb_cond_stack *ndb_cond,
+ TABLE *table, const NdbDictionary::Table *ndb_table);
+ int build_scan_filter_predicate(Ndb_cond* &cond,
+ NdbScanFilter* filter,
+ bool negated= false);
+ int build_scan_filter_group(Ndb_cond* &cond,
+ NdbScanFilter* filter);
+ int build_scan_filter(Ndb_cond* &cond, NdbScanFilter* filter);
+
+ Ndb_cond_stack *m_cond_stack;
+};
diff --git a/sql/item.cc b/sql/item.cc
index bd5e0ae1a8f..f339bad78e4 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3576,9 +3576,13 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
select->inner_refs_list.push_back(rf);
rf->in_sum_func= thd->lex->in_sum_func;
}
+ /*
+ A reference is resolved to a nest level that's outer or the same as
+ the nest level of the enclosing set function : adjust the value of
+ max_arg_level for the function if it's needed.
+ */
if (thd->lex->in_sum_func &&
- thd->lex->in_sum_func->nest_level ==
- thd->lex->current_select->nest_level)
+ thd->lex->in_sum_func->nest_level >= select->nest_level)
{
Item::Type ref_type= (*reference)->type();
set_if_bigger(thd->lex->in_sum_func->max_arg_level,
@@ -4237,6 +4241,21 @@ enum_field_types Item::field_type() const
}
+bool Item::is_datetime()
+{
+ switch (field_type())
+ {
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+
/*
Create a field to hold a string value from an item
@@ -5283,6 +5302,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last_checked_context->select_lex,
thd->lex->current_select, this, fld);
+ /*
+ A reference is resolved to a nest level that's outer or the same as
+ the nest level of the enclosing set function : adjust the value of
+ max_arg_level for the function if it's needed.
+ */
+ if (thd->lex->in_sum_func &&
+ thd->lex->in_sum_func->nest_level >=
+ last_checked_context->select_lex->nest_level)
+ set_if_bigger(thd->lex->in_sum_func->max_arg_level,
+ last_checked_context->select_lex->nest_level);
return FALSE;
}
if (ref == 0)
@@ -5296,6 +5325,16 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
DBUG_ASSERT(*ref && (*ref)->fixed);
mark_as_dependent(thd, last_checked_context->select_lex,
context->select_lex, this, this);
+ /*
+ A reference is resolved to a nest level that's outer or the same as
+ the nest level of the enclosing set function : adjust the value of
+ max_arg_level for the function if it's needed.
+ */
+ if (thd->lex->in_sum_func &&
+ thd->lex->in_sum_func->nest_level >=
+ last_checked_context->select_lex->nest_level)
+ set_if_bigger(thd->lex->in_sum_func->max_arg_level,
+ last_checked_context->select_lex->nest_level);
}
}
@@ -6228,6 +6267,14 @@ void Item_cache_int::store(Item *item)
}
+void Item_cache_int::store(Item *item, longlong val_arg)
+{
+ value= val_arg;
+ null_value= item->null_value;
+ unsigned_flag= item->unsigned_flag;
+}
+
+
String *Item_cache_int::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item.h b/sql/item.h
index 3d20aaf66cd..8b57f831cbd 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -942,6 +942,7 @@ public:
representation is more precise than the string one).
*/
virtual bool result_as_longlong() { return FALSE; }
+ bool is_datetime();
};
@@ -1840,33 +1841,57 @@ public:
/* for show tables */
-
-class Item_datetime :public Item_string
+class Item_partition_func_safe_string: public Item_string
{
public:
- Item_datetime(const char *item_name): Item_string(item_name,"",0,
- &my_charset_bin)
- { max_length=19;}
- enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ Item_partition_func_safe_string(const char *name, uint length,
+ CHARSET_INFO *cs= NULL):
+ Item_string(name, length, cs)
+ {}
bool check_partition_func_processor(byte *int_arg) {return TRUE;}
};
-class Item_empty_string :public Item_string
+
+class Item_return_date_time :public Item_partition_func_safe_string
+{
+ enum_field_types date_time_field_type;
+public:
+ Item_return_date_time(const char *name_arg, enum_field_types field_type_arg)
+ :Item_partition_func_safe_string(name_arg, 0, &my_charset_bin),
+ date_time_field_type(field_type_arg)
+ { }
+ enum_field_types field_type() const { return date_time_field_type; }
+};
+
+
+class Item_blob :public Item_partition_func_safe_string
+{
+public:
+ Item_blob(const char *name, uint length) :
+ Item_partition_func_safe_string(name, length, &my_charset_bin)
+ { max_length= length; }
+ enum Type type() const { return TYPE_HOLDER; }
+ enum_field_types field_type() const { return MYSQL_TYPE_BLOB; }
+};
+
+
+class Item_empty_string :public Item_partition_func_safe_string
{
public:
Item_empty_string(const char *header,uint length, CHARSET_INFO *cs= NULL) :
- Item_string("",0, cs ? cs : &my_charset_bin)
+ Item_partition_func_safe_string("",0, cs ? cs : &my_charset_bin)
{ name=(char*) header; max_length= cs ? length * cs->mbmaxlen : length; }
void make_field(Send_field *field);
};
+
class Item_return_int :public Item_int
{
enum_field_types int_field_type;
public:
Item_return_int(const char *name_arg, uint length,
- enum_field_types field_type_arg)
- :Item_int(name_arg, 0, length), int_field_type(field_type_arg)
+ enum_field_types field_type_arg, longlong value= 0)
+ :Item_int(name_arg, value, length), int_field_type(field_type_arg)
{
unsigned_flag=1;
}
@@ -2529,11 +2554,13 @@ public:
Item_cache_int(): Item_cache(), value(0) {}
void store(Item *item);
+ void store(Item *item, longlong val_arg);
double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; }
longlong val_int() { DBUG_ASSERT(fixed == 1); return value; }
String* val_str(String *str);
my_decimal *val_decimal(my_decimal *);
enum Item_result result_type() const { return INT_RESULT; }
+ bool result_as_longlong() { return TRUE; }
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 0a177a58dbd..ff7b991448c 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -437,7 +437,9 @@ void Item_bool_func2::fix_length_and_dec()
if (arg_real_item->type() == FIELD_ITEM)
{
Field *field=((Item_field*) arg_real_item)->field;
- if (field->can_be_compared_as_longlong())
+ if (field->can_be_compared_as_longlong() &&
+ !(arg_real_item->is_datetime() &&
+ args[1]->result_type() == STRING_RESULT))
{
if (convert_constant_item(thd, field,&args[1]))
{
@@ -452,7 +454,9 @@ void Item_bool_func2::fix_length_and_dec()
if (arg_real_item->type() == FIELD_ITEM)
{
Field *field=((Item_field*) arg_real_item)->field;
- if (field->can_be_compared_as_longlong())
+ if (field->can_be_compared_as_longlong() &&
+ !(arg_real_item->is_datetime() &&
+ args[0]->result_type() == STRING_RESULT))
{
if (convert_constant_item(thd, field,&args[0]))
{
@@ -571,6 +575,335 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
}
+/*
+ Convert date provided in a string to the int representation.
+
+ SYNOPSIS
+ get_date_from_str()
+ thd Thread handle
+ str a string to convert
+ warn_type type of the timestamp for issuing the warning
+ warn_name field name for issuing the warning
+ error_arg [out] TRUE if string isn't a DATETIME or clipping occur
+
+ DESCRIPTION
+ Convert date provided in the string str to the int representation.
+ if the string contains wrong date or doesn't contain it at all
+ then the warning is issued and TRUE returned in the error_arg argument.
+ The warn_type and the warn_name arguments are used as the name and the
+ type of the field when issuing the warning.
+
+ RETURN
+ converted value.
+*/
+
+static ulonglong
+get_date_from_str(THD *thd, String *str, timestamp_type warn_type,
+ char *warn_name, bool *error_arg)
+{
+ ulonglong value= 0;
+ int error;
+ MYSQL_TIME l_time;
+ enum_mysql_timestamp_type ret;
+ *error_arg= TRUE;
+
+ ret= str_to_datetime(str->ptr(), str->length(), &l_time,
+ (TIME_FUZZY_DATE | MODE_INVALID_DATES |
+ (thd->variables.sql_mode &
+ (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE))),
+ &error);
+ if ((ret == MYSQL_TIMESTAMP_DATETIME || ret == MYSQL_TIMESTAMP_DATE))
+ {
+ value= TIME_to_ulonglong_datetime(&l_time);
+ *error_arg= FALSE;
+ }
+
+ if (error || *error_arg)
+ {
+ make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ str->ptr(), str->length(),
+ warn_type, warn_name);
+ *error_arg= TRUE;
+ }
+ return value;
+}
+
+
+/*
+ Check whether compare_datetime() can be used to compare items.
+
+ SYNOPSIS
+ Arg_comparator::can_compare_as_dates()
+ a, b [in] items to be compared
+ const_value [out] converted value of the string constant, if any
+
+ DESCRIPTION
+ Check several cases when the DATE/DATETIME comparator should be used.
+ The following cases are checked:
+ 1. Both a and b is a DATE/DATETIME field/function returning string or
+ int result.
+ 2. Only a or b is a DATE/DATETIME field/function returning string or
+ int result and the other item (b or a) is an item with string result.
+ If the second item is a constant one then it's checked to be
+ convertible to the DATE/DATETIME type. If the constant can't be
+ converted to a DATE/DATETIME then the compare_datetime() comparator
+ isn't used and the warning about wrong DATE/DATETIME value is issued.
+ In all other cases (date-[int|real|decimal]/[int|real|decimal]-date)
+ the comparison is handled by other comparators.
+ If the datetime comparator can be used and one the operands of the
+ comparison is a string constant that was successfully converted to a
+ DATE/DATETIME type then the result of the conversion is returned in the
+ const_value if it is provided. If there is no constant or
+ compare_datetime() isn't applicable then the *const_value remains
+ unchanged.
+
+ RETURN
+ the found type of date comparison
+*/
+
+enum Arg_comparator::enum_date_cmp_type
+Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value)
+{
+ enum enum_date_cmp_type cmp_type= CMP_DATE_DFLT;
+ Item *str_arg= 0, *date_arg= 0;
+
+ if (a->type() == Item::ROW_ITEM || b->type() == Item::ROW_ITEM)
+ return CMP_DATE_DFLT;
+
+ if (a->is_datetime())
+ {
+ if (b->is_datetime())
+ cmp_type= CMP_DATE_WITH_DATE;
+ else if (b->result_type() == STRING_RESULT)
+ {
+ cmp_type= CMP_DATE_WITH_STR;
+ date_arg= a;
+ str_arg= b;
+ }
+ }
+ else if (b->is_datetime() && a->result_type() == STRING_RESULT)
+ {
+ cmp_type= CMP_STR_WITH_DATE;
+ date_arg= b;
+ str_arg= a;
+ }
+
+ if (cmp_type != CMP_DATE_DFLT)
+ {
+ if (cmp_type != CMP_DATE_WITH_DATE && str_arg->const_item())
+ {
+ THD *thd= current_thd;
+ ulonglong value;
+ bool error;
+ String tmp, *str_val= 0;
+ timestamp_type t_type= (date_arg->field_type() == MYSQL_TYPE_DATE ?
+ MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
+
+ str_val= str_arg->val_str(&tmp);
+ if (str_arg->null_value)
+ return CMP_DATE_DFLT;
+ value= get_date_from_str(thd, str_val, t_type, date_arg->name, &error);
+ if (error)
+ return CMP_DATE_DFLT;
+ if (const_value)
+ *const_value= value;
+ }
+ }
+ return cmp_type;
+}
+
+
+int Arg_comparator::set_cmp_func(Item_bool_func2 *owner_arg,
+ Item **a1, Item **a2,
+ Item_result type)
+{
+ enum enum_date_cmp_type cmp_type;
+ ulonglong const_value;
+ a= a1;
+ b= a2;
+
+ if ((cmp_type= can_compare_as_dates(*a, *b, &const_value)))
+ {
+ thd= current_thd;
+ owner= owner_arg;
+ a_type= (*a)->field_type();
+ b_type= (*b)->field_type();
+ a_cache= 0;
+ b_cache= 0;
+
+ if (cmp_type != CMP_DATE_WITH_DATE &&
+ ((*b)->const_item() || (*a)->const_item()))
+ {
+ Item_cache_int *cache= new Item_cache_int();
+ /* Mark the cache as non-const to prevent re-caching. */
+ cache->set_used_tables(1);
+ if (!(*a)->is_datetime())
+ {
+ cache->store((*a), const_value);
+ a_cache= cache;
+ a= (Item **)&a_cache;
+ }
+ else
+ {
+ cache->store((*b), const_value);
+ b_cache= cache;
+ b= (Item **)&b_cache;
+ }
+ }
+ is_nulls_eq= test(owner && owner->functype() == Item_func::EQUAL_FUNC);
+ func= &Arg_comparator::compare_datetime;
+ return 0;
+ }
+ return set_compare_func(owner_arg, type);
+}
+
+
+void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
+{
+ thd= current_thd;
+ /* A caller will handle null values by itself. */
+ owner= NULL;
+ a= a1;
+ b= b1;
+ a_type= (*a)->field_type();
+ b_type= (*b)->field_type();
+ a_cache= 0;
+ b_cache= 0;
+ is_nulls_eq= FALSE;
+ func= &Arg_comparator::compare_datetime;
+}
+
+/*
+ Retrieves correct DATETIME value from given item.
+
+ SYNOPSIS
+ get_datetime_value()
+ thd thread handle
+ item_arg [in/out] item to retrieve DATETIME value from
+ cache_arg [in/out] pointer to place to store the caching item to
+ warn_item [in] item for issuing the conversion warning
+ is_null [out] TRUE <=> the item_arg is null
+
+ DESCRIPTION
+ Retrieves the correct DATETIME value from given item for comparison by the
+ compare_datetime() function.
+ If item's result can be compared as longlong then its int value is used
+ and its string value is used otherwise. Strings are always parsed and
+ converted to int values by the get_date_from_str() function.
+ This allows us to compare correctly string dates with missed insignificant
+ zeros. If an item is a constant one then its value is cached and it isn't
+ get parsed again. An Item_cache_int object is used for caching values. It
+ seamlessly substitutes the original item. The cache item is marked as
+ non-constant to prevent re-caching it again. In order to compare
+ correctly DATE and DATETIME items the result of the former are treated as
+ a DATETIME with zero time (00:00:00).
+
+ RETURN
+ obtained value
+*/
+
+static ulonglong
+get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
+ Item *warn_item, bool *is_null)
+{
+ ulonglong value= 0;
+ String buf, *str= 0;
+ Item *item= **item_arg;
+
+ if (item->result_as_longlong())
+ {
+ value= item->val_int();
+ *is_null= item->null_value;
+ if (item->field_type() == MYSQL_TYPE_DATE)
+ value*= 1000000L;
+ }
+ else
+ {
+ str= item->val_str(&buf);
+ *is_null= item->null_value;
+ }
+ if (*is_null)
+ return -1;
+ /*
+ Convert strings to the integer DATE/DATETIME representation.
+ Even if both dates provided in strings we can't compare them directly as
+ strings as there is no warranty that they are correct and do not miss
+ some insignificant zeros.
+ */
+ if (str)
+ {
+ bool error;
+ enum_field_types f_type= warn_item->field_type();
+ timestamp_type t_type= f_type ==
+ MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME;
+ value= get_date_from_str(thd, str, t_type, warn_item->name, &error);
+ }
+ if (item->const_item())
+ {
+ Item_cache_int *cache= new Item_cache_int();
+ /* Mark the cache as non-const to prevent re-caching. */
+ cache->set_used_tables(1);
+ cache->store(item, value);
+ *cache_arg= cache;
+ *item_arg= cache_arg;
+ }
+ return value;
+}
+
+/*
+ Compare items values as dates.
+
+ SYNOPSIS
+ Arg_comparator::compare_datetime()
+
+ DESCRIPTION
+ Compare items values as DATE/DATETIME for both EQUAL_FUNC and from other
+ comparison functions. The correct DATETIME values are obtained
+ with help of the get_datetime_value() function.
+
+ RETURN
+ If is_nulls_eq is TRUE:
+ 1 if items are equal or both are null
+ 0 otherwise
+ If is_nulls_eq is FALSE:
+ -1 a < b or one of items is null
+ 0 a == b
+ 1 a > b
+*/
+
+int Arg_comparator::compare_datetime()
+{
+ bool is_null= FALSE;
+ ulonglong a_value, b_value;
+
+ /* Get DATE/DATETIME value of the 'a' item. */
+ a_value= get_datetime_value(thd, &a, &a_cache, *b, &is_null);
+ if (!is_nulls_eq && is_null)
+ {
+ if (owner)
+ owner->null_value= 1;
+ return -1;
+ }
+
+ /* Get DATE/DATETIME value of the 'b' item. */
+ b_value= get_datetime_value(thd, &b, &b_cache, *a, &is_null);
+ if (is_null)
+ {
+ if (owner)
+ owner->null_value= is_nulls_eq ? 0 : 1;
+ return is_nulls_eq ? 1 : -1;
+ }
+
+ if (owner)
+ owner->null_value= 0;
+
+ /* Compare values. */
+ if (is_nulls_eq)
+ return (a_value == b_value);
+ return a_value < b_value ? -1 : (a_value > b_value ? 1 : 0);
+}
+
+
int Arg_comparator::compare_string()
{
String *res1,*res2;
@@ -1420,8 +1753,10 @@ bool Item_func_between::fix_fields(THD *thd, Item **ref)
void Item_func_between::fix_length_and_dec()
{
- max_length= 1;
- THD *thd= current_thd;
+ max_length= 1;
+ int i;
+ bool datetime_found= FALSE;
+ compare_as_dates= TRUE;
/*
As some compare functions are generated after sql_yacc,
@@ -1436,26 +1771,29 @@ void Item_func_between::fix_length_and_dec()
return;
/*
- Make a special case of compare with date/time and longlong fields.
- They are compared as integers, so for const item this time-consuming
- conversion can be done only once, not for every single comparison
+ Detect the comparison of DATE/DATETIME items.
+ At least one of items should be a DATE/DATETIME item and other items
+ should return the STRING result.
*/
- if (args[0]->real_item()->type() == FIELD_ITEM &&
- thd->lex->sql_command != SQLCOM_CREATE_VIEW &&
- thd->lex->sql_command != SQLCOM_SHOW_CREATE)
+ for (i= 0; i < 3; i++)
{
- Field *field=((Item_field*) (args[0]->real_item()))->field;
- if (field->can_be_compared_as_longlong())
+ if (args[i]->is_datetime())
{
- /*
- The following can't be recoded with || as convert_constant_item
- changes the argument
- */
- if (convert_constant_item(thd, field,&args[1]))
- cmp_type=INT_RESULT; // Works for all types.
- if (convert_constant_item(thd, field,&args[2]))
- cmp_type=INT_RESULT; // Works for all types.
+ datetime_found= TRUE;
+ continue;
}
+ if (args[i]->result_type() == STRING_RESULT)
+ continue;
+ compare_as_dates= FALSE;
+ break;
+ }
+ if (!datetime_found)
+ compare_as_dates= FALSE;
+
+ if (compare_as_dates)
+ {
+ ge_cmp.set_datetime_cmp_func(args, args + 1);
+ le_cmp.set_datetime_cmp_func(args, args + 2);
}
}
@@ -1463,7 +1801,27 @@ void Item_func_between::fix_length_and_dec()
longlong Item_func_between::val_int()
{ // ANSI BETWEEN
DBUG_ASSERT(fixed == 1);
- if (cmp_type == STRING_RESULT)
+ if (compare_as_dates)
+ {
+ int ge_res, le_res;
+
+ ge_res= ge_cmp.compare();
+ if ((null_value= args[0]->null_value))
+ return 0;
+ le_res= le_cmp.compare();
+
+ if (!args[1]->null_value && !args[2]->null_value)
+ return (longlong) ((ge_res >= 0 && le_res <=0) != negated);
+ else if (args[1]->null_value)
+ {
+ null_value= le_res > 0; // not null if false range.
+ }
+ else
+ {
+ null_value= ge_res < 0;
+ }
+ }
+ else if (cmp_type == STRING_RESULT)
{
String *value,*a,*b;
value=args[0]->val_str(&value0);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f35b6a126ed..7aede7d2954 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -35,12 +35,19 @@ class Arg_comparator: public Sql_alloc
Item_bool_func2 *owner;
Arg_comparator *comparators; // used only for compare_row()
double precision;
-
+ /* Fields used in DATE/DATETIME comparison. */
+ THD *thd;
+ enum_field_types a_type, b_type; // Types of a and b items
+ Item *a_cache, *b_cache; // Cached values of a and b items
+ bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC
+ enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE,
+ CMP_DATE_WITH_STR, CMP_STR_WITH_DATE };
public:
DTCollation cmp_collation;
- Arg_comparator() {};
- Arg_comparator(Item **a1, Item **a2): a(a1), b(a2) {};
+ Arg_comparator(): thd(0), a_cache(0), b_cache(0) {};
+ Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), thd(0),
+ a_cache(0), b_cache(0) {};
int set_compare_func(Item_bool_func2 *owner, Item_result type);
inline int set_compare_func(Item_bool_func2 *owner_arg)
@@ -48,14 +55,10 @@ public:
return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
(*b)->result_type()));
}
- inline int set_cmp_func(Item_bool_func2 *owner_arg,
+ int set_cmp_func(Item_bool_func2 *owner_arg,
Item **a1, Item **a2,
- Item_result type)
- {
- a= a1;
- b= a2;
- return set_compare_func(owner_arg, type);
- }
+ Item_result type);
+
inline int set_cmp_func(Item_bool_func2 *owner_arg,
Item **a1, Item **a2)
{
@@ -83,7 +86,12 @@ public:
int compare_e_row(); // compare args[0] & args[1]
int compare_real_fixed();
int compare_e_real_fixed();
+ int compare_datetime(); // compare args[0] & args[1] as DATETIMEs
+
+ static enum enum_date_cmp_type can_compare_as_dates(Item *a, Item *b,
+ ulonglong *const_val_arg);
+ void set_datetime_cmp_func(Item **a1, Item **b1);
static arg_cmp_func comparator_matrix [5][2];
friend class Item_func;
@@ -574,8 +582,12 @@ class Item_func_between :public Item_func_opt_neg
public:
Item_result cmp_type;
String value0,value1,value2;
+ /* TRUE <=> arguments will be compared as dates. */
+ bool compare_as_dates;
+ /* Comparators used for DATE/DATETIME comparison. */
+ Arg_comparator ge_cmp, le_cmp;
Item_func_between(Item *a, Item *b, Item *c)
- :Item_func_opt_neg(a, b, c) {}
+ :Item_func_opt_neg(a, b, c), compare_as_dates(FALSE) {}
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_KEY; }
enum Functype functype() const { return BETWEEN; }
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 79b09967a9d..812d3c222c0 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -82,7 +82,7 @@ void Item_subselect::init(st_select_lex *select_lex,
parsing_place= (outer_select->in_sum_expr ?
NO_MATTER :
outer_select->parsing_place);
- if (select_lex->next_select())
+ if (unit->is_union())
engine= new subselect_union_engine(unit, result, this);
else
engine= new subselect_single_select_engine(select_lex, result, this);
@@ -412,7 +412,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
SELECT_LEX *select_lex= join->select_lex;
Query_arena *arena= thd->stmt_arena;
- if (!select_lex->master_unit()->first_select()->next_select() &&
+ if (!select_lex->master_unit()->is_union() &&
!select_lex->table_list.elements &&
select_lex->item_list.elements == 1 &&
!select_lex->item_list.head()->with_sum_func &&
@@ -1147,7 +1147,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
else
{
bool tmp;
- if (select_lex->master_unit()->first_select()->next_select())
+ if (select_lex->master_unit()->is_union())
{
/*
comparison functions can't be changed during fix_fields()
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index f217a6ea953..2eced347650 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -175,13 +175,25 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref)
MYF(0));
return TRUE;
}
- if (in_sum_func && in_sum_func->nest_level == nest_level)
+ if (in_sum_func)
{
/*
If the set function is nested adjust the value of
max_sum_func_level for the nesting set function.
+ We take into account only enclosed set functions that are to be
+ aggregated on the same level or above of the nest level of
+ the enclosing set function.
+ But we must always pass up the max_sum_func_level because it is
+ the maximum nested level of all directly and indirectly enclosed
+ set functions. We must do that even for set functions that are
+ aggregated inside of their enclosing set function's nest level
+ because the enclosing function may contain another enclosing
+ function that is to be aggregated outside or on the same level
+ as its parent's nest level.
*/
- set_if_bigger(in_sum_func->max_sum_func_level, aggr_level);
+ if (in_sum_func->nest_level >= aggr_level)
+ set_if_bigger(in_sum_func->max_sum_func_level, aggr_level);
+ set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level);
}
update_used_tables();
thd->lex->in_sum_func= in_sum_func;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 4d6ca2a9b3e..0fd57264784 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -2056,7 +2056,30 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if (date_sub_interval)
interval.neg = !interval.neg;
- return (null_value= date_add_interval(ltime, int_type, interval));
+ if ((null_value= date_add_interval(ltime, int_type, interval)))
+ return 1;
+
+ /* Adjust cached_field_type according to the detected type. */
+ if (cached_field_type == MYSQL_TYPE_STRING)
+ {
+ switch (ltime->time_type)
+ {
+ case MYSQL_TIMESTAMP_DATE:
+ cached_field_type= MYSQL_TYPE_DATE;
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ cached_field_type= MYSQL_TYPE_DATETIME;
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ cached_field_type= MYSQL_TYPE_TIME;
+ break;
+ default:
+ /* Shouldn't get here. */
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+ return 0;
}
diff --git a/sql/key.cc b/sql/key.cc
index faa7bf1f04b..19861cee134 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -139,29 +139,22 @@ void key_copy(byte *to_key, byte *from_record, KEY *key_info, uint key_length)
key_length--;
}
}
- if (key_part->key_part_flag & HA_BLOB_PART)
- {
- char *pos;
- ulong blob_length= ((Field_blob*) key_part->field)->get_length();
- key_length-= HA_KEY_BLOB_LENGTH;
- ((Field_blob*) key_part->field)->get_ptr(&pos);
- length=min(key_length, key_part->length);
- set_if_smaller(blob_length, length);
- int2store(to_key, (uint) blob_length);
- to_key+= HA_KEY_BLOB_LENGTH; // Skip length info
- memcpy(to_key, pos, blob_length);
- }
- else if (key_part->key_part_flag & HA_VAR_LENGTH_PART)
+ if (key_part->key_part_flag & HA_BLOB_PART ||
+ key_part->key_part_flag & HA_VAR_LENGTH_PART)
{
key_length-= HA_KEY_BLOB_LENGTH;
length= min(key_length, key_part->length);
- key_part->field->get_key_image((char *) to_key, length, Field::itRAW);
+ key_part->field->get_key_image((char*) to_key, length, Field::itRAW);
to_key+= HA_KEY_BLOB_LENGTH;
}
else
{
length= min(key_length, key_part->length);
- memcpy(to_key, from_record + key_part->offset, (size_t) length);
+ Field *field= key_part->field;
+ CHARSET_INFO *cs= field->charset();
+ uint bytes= field->get_key_image((char*) to_key, length, Field::itRAW);
+ if (bytes < length)
+ cs->cset->fill(cs, (char*) to_key + bytes, length - bytes, ' ');
}
to_key+= length;
key_length-= length;
diff --git a/sql/sp.cc b/sql/sp.cc
index 49b8b304e76..d96e4295a83 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -784,7 +784,9 @@ db_show_routine_status(THD *thd, int type, const char *wild)
{
switch (used_field->field_type) {
case MYSQL_TYPE_TIMESTAMP:
- field_list.push_back(item=new Item_datetime(used_field->field_name));
+ field_list.push_back(item=
+ new Item_return_date_time(used_field->field_name,
+ MYSQL_TYPE_DATETIME));
break;
default:
field_list.push_back(item=new Item_empty_string(used_field->field_name,
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0f428da1fdb..f26d6e5777b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -5575,6 +5575,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
*/
arena= thd->activate_stmt_arena_if_needed(&backup);
+ thd->lex->current_select->cur_pos_in_select_list= 0;
while (wild_num && (item= it++))
{
if (item->type() == Item::FIELD_ITEM &&
@@ -5616,7 +5617,10 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
}
wild_num--;
}
+ else
+ thd->lex->current_select->cur_pos_in_select_list++;
}
+ thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
if (arena)
{
/* make * substituting permanent */
@@ -6099,6 +6103,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
}
else
thd->used_tables|= item->used_tables();
+ thd->lex->current_select->cur_pos_in_select_list++;
}
/*
In case of stored tables, all fields are considered as used,
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 89bd7958c86..ea7545fe5cb 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -236,9 +236,7 @@ bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
SELECT_LEX *first_select= unit->first_select();
select_union *derived_result= orig_table_list->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
- bool is_union= first_select->next_select() &&
- first_select->next_select()->linkage == UNION_TYPE;
- if (is_union)
+ if (unit->is_union())
{
// execute union without clean up
res= unit->exec();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 1175776ffd3..5a0e6bfdd0c 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -796,7 +796,6 @@ private:
};
typedef class st_select_lex SELECT_LEX;
-
inline bool st_select_lex_unit::is_union ()
{
return first_select()->next_select() &&
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d095fc277c8..af80ea91397 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5966,7 +5966,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
fake_select_lex->context.resolve_in_select_list= TRUE;
fake_select_lex->context.select_lex= fake_select_lex;
- if (!first_sl->next_select())
+ if (!is_union())
{
/*
This works only for
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index da2c8e96b0f..ca127297481 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -231,7 +231,8 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
register SELECT_LEX *select_lex = &lex->select_lex;
DBUG_ENTER("handle_select");
- if (select_lex->next_select() || select_lex->master_unit()->fake_select_lex)
+ if (select_lex->master_unit()->is_union() ||
+ select_lex->master_unit()->fake_select_lex)
res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option);
else
{
@@ -442,7 +443,7 @@ JOIN::prepare(Item ***rref_pointer_array,
select_lex= select_lex_arg;
select_lex->join= this;
join_list= &select_lex->top_join_list;
- union_part= (unit_arg->first_select()->next_select() != 0);
+ union_part= unit_arg->is_union();
thd->lex->current_select->is_item_list_lookup= 1;
/*
@@ -1191,7 +1192,7 @@ JOIN::optimize()
if (!group_list && !order &&
unit->item && unit->item->substype() == Item_subselect::IN_SUBS &&
tables == 1 && conds &&
- !unit->first_select()->next_select())
+ !unit->is_union())
{
if (!having)
{
@@ -3165,7 +3166,7 @@ add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
if (!join->group_list && !join->order &&
join->unit->item &&
join->unit->item->substype() == Item_subselect::IN_SUBS &&
- !join->unit->first_select()->next_select())
+ !join->unit->is_union())
{
KEY_FIELD *save= *key_fields;
add_key_fields(join, key_fields, and_level, cond_arg, usable_tables,
@@ -5614,8 +5615,9 @@ static void add_not_null_conds(JOIN *join)
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
- if ((tab->type == JT_REF || tab->type == JT_REF_OR_NULL) &&
- !tab->table->maybe_null)
+ if ((tab->type == JT_REF || tab->type == JT_EQ_REF ||
+ tab->type == JT_REF_OR_NULL) &&
+ !tab->table->maybe_null)
{
for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++)
{
@@ -8856,17 +8858,13 @@ static bool
test_if_equality_guarantees_uniqueness(Item *l, Item *r)
{
return r->const_item() &&
- /* elements must be of the same result type */
- (r->result_type() == l->result_type() ||
- /* or dates compared to longs */
- (((l->type() == Item::FIELD_ITEM &&
- ((Item_field *)l)->field->can_be_compared_as_longlong()) ||
- (l->type() == Item::FUNC_ITEM &&
- ((Item_func *)l)->result_as_longlong())) &&
- r->result_type() == INT_RESULT))
- /* and must have the same collation if compared as strings */
- && (l->result_type() != STRING_RESULT ||
- l->collation.collation == r->collation.collation);
+ /* elements must be compared as dates */
+ (Arg_comparator::can_compare_as_dates(l, r, 0) ||
+ /* or of the same result type */
+ (r->result_type() == l->result_type() &&
+ /* and must have the same collation if compared as strings */
+ (l->result_type() != STRING_RESULT ||
+ l->collation.collation == r->collation.collation)));
}
/*
@@ -15523,7 +15521,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
"UNION")));
sl->options|= SELECT_DESCRIBE;
}
- if (first->next_select())
+ if (unit->is_union())
{
unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
unit->fake_select_lex->type= "UNION RESULT";
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index b28077300f3..e4e0fb22a5e 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -4704,16 +4704,26 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
for (; fields_info->field_name; fields_info++)
{
switch (fields_info->field_type) {
+ case MYSQL_TYPE_TINY:
case MYSQL_TYPE_LONG:
- if (!(item= new Item_int(fields_info->field_name,
- fields_info->value,
- fields_info->field_length)))
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ if (!(item= new Item_return_int(fields_info->field_name,
+ fields_info->field_length,
+ fields_info->field_type,
+ fields_info->value)))
{
DBUG_RETURN(0);
}
+ item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
break;
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
case MYSQL_TYPE_TIMESTAMP:
- if (!(item=new Item_datetime(fields_info->field_name)))
+ case MYSQL_TYPE_DATETIME:
+ if (!(item=new Item_return_date_time(fields_info->field_name,
+ fields_info->field_type)))
{
DBUG_RETURN(0);
}
@@ -4729,7 +4739,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
{
DBUG_RETURN(0);
}
- item->unsigned_flag= (fields_info->field_length/10000)%10;
+ item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED);
item->decimals= fields_info->field_length%10;
item->max_length= (fields_info->field_length/100)%100;
if (item->unsigned_flag == 0)
@@ -4739,6 +4749,16 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
item->set_name(fields_info->field_name,
strlen(fields_info->field_name), cs);
break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ if (!(item= new Item_blob(fields_info->field_name,
+ fields_info->field_length)))
+ {
+ DBUG_RETURN(0);
+ }
+ break;
default:
/* Don't let unimplemented types pass through. Could be a grave error. */
DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING);
@@ -4754,7 +4774,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
break;
}
field_list.push_back(item);
- item->maybe_null= fields_info->maybe_null;
+ item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL);
field_count++;
}
TMP_TABLE_PARAM *tmp_table_param =
@@ -5374,25 +5394,29 @@ ST_FIELD_INFO tables_fields_info[]=
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name"},
{"TABLE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine"},
- {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Version"},
+ {"VERSION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"},
{"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"},
- {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Rows"},
- {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Avg_row_length"},
- {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Data_length"},
- {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Max_data_length"},
- {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Index_length"},
- {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Data_free"},
- {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- "Auto_increment"},
- {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"},
- {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"},
- {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"},
+ {"TABLE_ROWS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"},
+ {"AVG_ROW_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"},
+ {"DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"},
+ {"MAX_DATA_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"},
+ {"INDEX_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"},
+ {"DATA_FREE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"},
+ {"AUTO_INCREMENT", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment"},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"},
{"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
- {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, "Checksum"},
+ {"CHECKSUM", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"},
{"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"},
{"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
@@ -5405,15 +5429,20 @@ ST_FIELD_INFO columns_fields_info[]=
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field"},
- {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 0, 0},
- {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, "Default"},
+ {"ORDINAL_POSITION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0,
+ MY_I_S_UNSIGNED, 0},
+ {"COLUMN_DEFAULT", MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0,
+ 1, "Default"},
{"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
{"DATA_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1,
- 0},
- {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
- {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
- {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONG, 0, 1, 0},
+ {"CHARACTER_MAXIMUM_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"CHARACTER_OCTET_LENGTH", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"NUMERIC_PRECISION", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"NUMERIC_SCALE", MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG,
+ 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
{"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0},
{"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"},
{"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"},
@@ -5430,7 +5459,7 @@ ST_FIELD_INFO charsets_fields_info[]=
{"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
{"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"},
{"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"},
- {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"},
+ {"MAXLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};
@@ -5439,10 +5468,10 @@ ST_FIELD_INFO collation_fields_info[]=
{
{"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"},
{"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"},
- {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Id"},
+ {"ID", MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id"},
{"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"},
{"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"},
- {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"},
+ {"SORTLEN", 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};
@@ -5469,19 +5498,19 @@ ST_FIELD_INFO events_fields_info[]=
{"EVENT_BODY", 8, MYSQL_TYPE_STRING, 0, 0, 0},
{"EVENT_DEFINITION", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
{"EVENT_TYPE", 9, MYSQL_TYPE_STRING, 0, 0, "Type"},
- {"EXECUTE_AT", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Execute at"},
+ {"EXECUTE_AT", 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at"},
{"INTERVAL_VALUE", 256, MYSQL_TYPE_STRING, 0, 1, "Interval value"},
{"INTERVAL_FIELD", 18, MYSQL_TYPE_STRING, 0, 1, "Interval field"},
{"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
- {"STARTS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Starts"},
- {"ENDS", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Ends"},
+ {"STARTS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts"},
+ {"ENDS", 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends"},
{"STATUS", 18, MYSQL_TYPE_STRING, 0, 0, "Status"},
{"ON_COMPLETION", 12, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0},
- {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0},
- {"LAST_EXECUTED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0},
+ {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, 0},
+ {"LAST_EXECUTED", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
{"EVENT_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ORIGINATOR", 10, MYSQL_TYPE_LONG, 0, 0, "Originator"},
+ {"ORIGINATOR", 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};
@@ -5512,8 +5541,8 @@ ST_FIELD_INFO proc_fields_info[]=
{"SQL_DATA_ACCESS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"SQL_PATH", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"},
- {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Created"},
- {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Modified"},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Created"},
+ {"LAST_ALTERED", 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified"},
{"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0},
{"ROUTINE_COMMENT", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"},
{"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"},
@@ -5526,14 +5555,15 @@ ST_FIELD_INFO stat_fields_info[]=
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"NON_UNIQUE", 1, MYSQL_TYPE_LONG, 0, 0, "Non_unique"},
+ {"NON_UNIQUE", 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique"},
{"INDEX_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"INDEX_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name"},
- {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONG, 0, 0, "Seq_in_index"},
+ {"SEQ_IN_INDEX", 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index"},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name"},
{"COLLATION", 1, MYSQL_TYPE_STRING, 0, 1, "Collation"},
- {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 1, "Cardinality"},
- {"SUB_PART", 3, MYSQL_TYPE_LONG, 0, 1, "Sub_part"},
+ {"CARDINALITY", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1,
+ "Cardinality"},
+ {"SUB_PART", 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part"},
{"PACKED", 10, MYSQL_TYPE_STRING, 0, 1, "Packed"},
{"NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"},
{"INDEX_TYPE", 16, MYSQL_TYPE_STRING, 0, 0, "Index_type"},
@@ -5623,8 +5653,8 @@ ST_FIELD_INFO key_column_usage_fields_info[]=
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
- {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONG, 0, 0, 0},
- {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONG, 0, 1, 0},
+ {"ORDINAL_POSITION", 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0},
+ {"POSITION_IN_UNIQUE_CONSTRAINT", 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0},
{"REFERENCED_TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"REFERENCED_TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"REFERENCED_COLUMN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
@@ -5646,8 +5676,8 @@ ST_FIELD_INFO open_tables_fields_info[]=
{
{"Database", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"},
{"Table",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"In_use", 1, MYSQL_TYPE_LONG, 0, 0, "In_use"},
- {"Name_locked", 4, MYSQL_TYPE_LONG, 0, 0, "Name_locked"},
+ {"In_use", 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use"},
+ {"Name_locked", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};
@@ -5661,7 +5691,7 @@ ST_FIELD_INFO triggers_fields_info[]=
{"EVENT_OBJECT_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"EVENT_OBJECT_SCHEMA",NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"EVENT_OBJECT_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table"},
- {"ACTION_ORDER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
+ {"ACTION_ORDER", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0},
{"ACTION_CONDITION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
{"ACTION_STATEMENT", 65535, MYSQL_TYPE_STRING, 0, 0, "Statement"},
{"ACTION_ORIENTATION", 9, MYSQL_TYPE_STRING, 0, 0, 0},
@@ -5670,7 +5700,7 @@ ST_FIELD_INFO triggers_fields_info[]=
{"ACTION_REFERENCE_NEW_TABLE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"ACTION_REFERENCE_OLD_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
{"ACTION_REFERENCE_NEW_ROW", 3, MYSQL_TYPE_STRING, 0, 0, 0},
- {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Created"},
+ {"CREATED", 0, MYSQL_TYPE_DATETIME, 0, 1, "Created"},
{"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, "sql_mode"},
{"DEFINER", 65535, MYSQL_TYPE_STRING, 0, 0, "Definer"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
@@ -5684,23 +5714,27 @@ ST_FIELD_INFO partitions_fields_info[]=
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"PARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"SUBPARTITION_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
- {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
+ {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
{"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0},
{"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0},
{"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
{"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
{"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0},
- {"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
- {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
- {"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
- {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
- {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
- {"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 0, 0},
- {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, 0},
+ {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0},
+ {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0},
+ {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0},
+ {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0},
+ {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
{"PARTITION_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, 0},
{"NODEGROUP", 12 , MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
@@ -5734,12 +5768,12 @@ ST_FIELD_INFO system_variables_fields_info[]=
ST_FIELD_INFO processlist_fields_info[]=
{
- {"ID", 4, MYSQL_TYPE_LONG, 0, 0, "Id"},
+ {"ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id"},
{"USER", 16, MYSQL_TYPE_STRING, 0, 0, "User"},
{"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host"},
{"DB", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db"},
{"COMMAND", 16, MYSQL_TYPE_STRING, 0, 0, "Command"},
- {"TIME", 7, MYSQL_TYPE_LONG, 0, 0, "Time"},
+ {"TIME", 7, MYSQL_TYPE_LONGLONG, 0, 0, "Time"},
{"STATE", 64, MYSQL_TYPE_STRING, 0, 1, "State"},
{"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info"},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
@@ -5763,7 +5797,7 @@ ST_FIELD_INFO plugin_fields_info[]=
ST_FIELD_INFO files_fields_info[]=
{
- {"FILE_ID", 4, MYSQL_TYPE_LONG, 0, 0, 0},
+ {"FILE_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0},
{"FILE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLESPACE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
@@ -5771,34 +5805,45 @@ ST_FIELD_INFO files_fields_info[]=
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
{"LOGFILE_GROUP_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONG, 0, 1, 0},
+ {"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
{"ENGINE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"FULLTEXT_KEYS", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0},
- {"DELETED_ROWS", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"UPDATE_COUNT", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"FREE_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"EXTENT_SIZE", 4, MYSQL_TYPE_LONG, 0, 0, 0},
- {"INITIAL_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0},
- {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0},
- {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONG, 0, 1, 0},
- {"CREATION_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, 0},
- {"RECOVER_TIME", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONG, 0, 1, 0},
- {"VERSION", 21 , MYSQL_TYPE_LONG, 0, 1, "Version"},
+ {"DELETED_ROWS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"UPDATE_COUNT", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"FREE_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"EXTENT_SIZE", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0},
+ {"INITIAL_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0},
+ {"CREATION_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, 0},
+ {"RECOVER_TIME", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONGLONG, 0, 1, 0},
+ {"VERSION", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version"},
{"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"},
- {"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 1, "Rows"},
- {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Avg_row_length"},
- {"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_length"},
- {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Max_data_length"},
- {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Index_length"},
- {"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_free"},
- {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"},
- {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"},
- {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"},
- {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"},
+ {"TABLE_ROWS", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows"},
+ {"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length"},
+ {"DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length"},
+ {"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length"},
+ {"INDEX_LENGTH", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length"},
+ {"DATA_FREE", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free"},
+ {"CREATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time"},
+ {"UPDATE_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time"},
+ {"CHECK_TIME", 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time"},
+ {"CHECKSUM", 21 , MYSQL_TYPE_LONGLONG, 0,
+ (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum"},
{"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0},
{"EXTRA", 255, MYSQL_TYPE_STRING, 0, 1, 0},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index bc81679a7fd..2a1db422058 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -165,7 +165,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
SELECT_LEX *sl, *first_sl= first_select();
select_result *tmp_result;
- bool is_union;
+ bool is_union_select;
TABLE *empty_table= 0;
DBUG_ENTER("st_select_lex_unit::prepare");
@@ -203,11 +203,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= sl= first_sl;
found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
- is_union= first_sl->next_select() || fake_select_lex;
+ is_union_select= is_union() || fake_select_lex;
/* Global option */
- if (is_union)
+ if (is_union_select)
{
if (!(tmp_result= union_result= new select_union))
goto err;
@@ -238,7 +238,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= sl;
- can_skip_order_by= is_union && !(sl->braces && sl->explicit_limit);
+ can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit);
saved_error= join->prepare(&sl->ref_pointer_array,
(TABLE_LIST*) sl->table_list.first,
@@ -251,7 +251,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
(ORDER*) 0 : (ORDER *)sl->order_list.first,
(ORDER*) sl->group_list.first,
sl->having,
- (is_union ? (ORDER*) 0 :
+ (is_union_select ? (ORDER*) 0 :
(ORDER*) thd_arg->lex->proc_list.first),
sl, this);
/* There are no * in the statement anymore (for PS) */
@@ -264,7 +264,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Use items list of underlaid select for derived tables to preserve
information about fields lengths and exact types
*/
- if (!is_union)
+ if (!is_union_select)
types= first_sl->item_list;
else if (sl == first_sl)
{
@@ -307,7 +307,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
- if (is_union)
+ if (is_union_select)
{
/*
Check that it was possible to aggregate
@@ -639,7 +639,7 @@ void st_select_lex_unit::reinit_exec_mechanism()
{
prepared= optimized= executed= 0;
#ifndef DBUG_OFF
- if (first_select()->next_select())
+ if (is_union())
{
List_iterator_fast<Item> it(item_list);
Item *field;
@@ -706,7 +706,6 @@ bool st_select_lex_unit::change_result(select_subselect *new_result,
List<Item> *st_select_lex_unit::get_unit_column_types()
{
SELECT_LEX *sl= first_select();
- bool is_union= test(sl->next_select());
bool is_procedure= test(sl->join->procedure);
if (is_procedure)
@@ -717,7 +716,7 @@ List<Item> *st_select_lex_unit::get_unit_column_types()
}
- if (is_union)
+ if (is_union())
{
DBUG_ASSERT(prepared);
/* Types are generated during prepare */
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 453cbbc091e..9dc7d2100d6 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -829,7 +829,7 @@ loop_out:
UNION
*/
if (view->updatable_view &&
- !lex->select_lex.next_select() &&
+ !lex->select_lex.master_unit()->is_union() &&
!((TABLE_LIST*)lex->select_lex.table_list.first)->next_local &&
find_table_in_global_list(lex->query_tables->next_global,
lex->query_tables->db,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 0c7d2fc2187..87cd33117dc 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -7817,7 +7817,7 @@ order_clause:
yet.
*/
SELECT_LEX *first_sl= unit->first_select();
- if (!first_sl->next_select() &&
+ if (!unit->is_union() &&
(first_sl->order_list.elements ||
first_sl->select_limit) &&
unit->add_fake_select_lex(lex->thd))
diff --git a/sql/table.cc b/sql/table.cc
index 39bdbb4cbb9..9be98eb14b9 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3552,7 +3552,16 @@ const char *Field_iterator_table::name()
Item *Field_iterator_table::create_item(THD *thd)
{
- return new Item_field(thd, &thd->lex->current_select->context, *ptr);
+ SELECT_LEX *select= thd->lex->current_select;
+
+ Item_field *item= new Item_field(thd, &select->context, *ptr);
+ if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY &&
+ !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS)
+ {
+ select->non_agg_fields.push_back(item);
+ item->marker= select->cur_pos_in_select_list;
+ }
+ return item;
}
diff --git a/sql/table.h b/sql/table.h
index bb9ced2e450..5b6aa2a8a7c 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -554,13 +554,17 @@ enum enum_schema_tables
};
+#define MY_I_S_MAYBE_NULL 1
+#define MY_I_S_UNSIGNED 2
+
+
typedef struct st_field_info
{
const char* field_name;
uint field_length;
enum enum_field_types field_type;
int value;
- bool maybe_null;
+ uint field_flags; // Field atributes(maybe_null, signed, unsigned etc.)
const char* old_name;
} ST_FIELD_INFO;
diff --git a/sql/time.cc b/sql/time.cc
index 249de3b879b..c552d085f5e 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -860,8 +860,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
}
break;
default:
- return 1;
+ goto null_date;
}
+
return 0; // Ok
invalid_date:
@@ -869,6 +870,7 @@ invalid_date:
ER_DATETIME_FUNCTION_OVERFLOW,
ER(ER_DATETIME_FUNCTION_OVERFLOW),
"datetime");
+null_date:
return 1;
}
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 18f930bf7da..de9cf06fe3a 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -3520,26 +3520,28 @@ no_commit:
/* This call will update the counter according to the
value that was inserted in the table */
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
-
- /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
- key error themselves, and we must update the autoinc counter if we are
- performing those statements. */
-
- if (error == DB_DUPLICATE_KEY && auto_inc_used
- && (user_thd->lex->sql_command == SQLCOM_REPLACE
- || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
- || (user_thd->lex->sql_command == SQLCOM_LOAD
- && user_thd->lex->duplicates == DUP_REPLACE))) {
-
- auto_inc = table->next_number_field->val_int();
-
- if (auto_inc != 0) {
- dict_table_autoinc_update(prebuilt->table, auto_inc);
- }
- }
+ dict_table_autoinc_update(prebuilt->table, auto_inc);
+ }
+ }
+
+ /* A REPLACE command and LOAD DATA INFILE REPLACE handle a duplicate
+ key error themselves, and we must update the autoinc counter if we are
+ performing those statements. */
+
+ if (error == DB_DUPLICATE_KEY && auto_inc_used
+ && (user_thd->lex->sql_command == SQLCOM_REPLACE
+ || user_thd->lex->sql_command == SQLCOM_REPLACE_SELECT
+ || (user_thd->lex->sql_command == SQLCOM_INSERT
+ && user_thd->lex->duplicates == DUP_UPDATE)
+ || (user_thd->lex->sql_command == SQLCOM_LOAD
+ && user_thd->lex->duplicates == DUP_REPLACE))) {
+
+ auto_inc = table->next_number_field->val_int();
+
+ if (auto_inc != 0) {
+ dict_table_autoinc_update(prebuilt->table, auto_inc);
+ }
+ }
innodb_srv_conc_exit_innodb(prebuilt->trx);
diff --git a/storage/ndb/include/kernel/signaldata/DictTabInfo.hpp b/storage/ndb/include/kernel/signaldata/DictTabInfo.hpp
index 57b4f0e7520..3923d8e6fbf 100644
--- a/storage/ndb/include/kernel/signaldata/DictTabInfo.hpp
+++ b/storage/ndb/include/kernel/signaldata/DictTabInfo.hpp
@@ -118,6 +118,7 @@ public:
FrmData = 27,
TableTemporaryFlag = 28, //Default not Temporary
+ ForceVarPartFlag = 29,
FragmentCount = 128, // No of fragments in table (!fragment replicas)
FragmentDataLen = 129,
@@ -301,6 +302,7 @@ public:
Uint32 PrimaryTableId;
Uint32 TableLoggedFlag;
Uint32 TableTemporaryFlag;
+ Uint32 ForceVarPartFlag;
Uint32 NoOfKeyAttr;
Uint32 NoOfAttributes;
Uint32 NoOfNullable;
diff --git a/storage/ndb/include/kernel/signaldata/LqhFrag.hpp b/storage/ndb/include/kernel/signaldata/LqhFrag.hpp
index 40ed73ad2e7..d4f4877cc5b 100644
--- a/storage/ndb/include/kernel/signaldata/LqhFrag.hpp
+++ b/storage/ndb/include/kernel/signaldata/LqhFrag.hpp
@@ -105,7 +105,7 @@ class LqhFragReq {
friend bool printLQH_FRAG_REQ(FILE *, const Uint32 *, Uint32, Uint16);
public:
- STATIC_CONST( SignalLength = 24 );
+ STATIC_CONST( SignalLength = 25 );
enum RequestInfo {
CreateInRunning = 0x8000000,
@@ -143,6 +143,7 @@ private:
Uint32 maxRowsHigh;
Uint32 minRowsLow;
Uint32 minRowsHigh;
+ Uint32 forceVarPartFlag;
};
class LqhFragConf {
diff --git a/storage/ndb/include/kernel/signaldata/ScanTab.hpp b/storage/ndb/include/kernel/signaldata/ScanTab.hpp
index 571fc374eab..0074078533f 100644
--- a/storage/ndb/include/kernel/signaldata/ScanTab.hpp
+++ b/storage/ndb/include/kernel/signaldata/ScanTab.hpp
@@ -115,16 +115,16 @@ private:
z = Descending (TUX) - 1 Bit 14
x = Range Scan (TUX) - 1 Bit 15
b = Scan batch - 10 Bit 16-25 (max 1023)
- d = Distribution key flag
- n = No disk flag
+ d = Distribution key flag - 1 Bit 26
+ n = No disk flag - 1 Bit 9
1111111111222222222233
01234567890123456789012345678901
- pppppppplnhcktzxbbbbbbbbbb
+ pppppppplnhcktzxbbbbbbbbbbd
*/
-#define PARALLELL_SHIFT (0)
-#define PARALLELL_MASK (255)
+#define PARALLEL_SHIFT (0)
+#define PARALLEL_MASK (255)
#define LOCK_MODE_SHIFT (8)
#define LOCK_MODE_MASK (1)
@@ -151,13 +151,15 @@ private:
#define SCAN_BATCH_MASK (1023)
#define SCAN_DISTR_KEY_SHIFT (26)
+#define SCAN_DISTR_KEY_MASK (1)
#define SCAN_NODISK_SHIFT (9)
+#define SCAN_NODISK_MASK (1)
inline
Uint8
ScanTabReq::getParallelism(const UintR & requestInfo){
- return (Uint8)((requestInfo >> PARALLELL_SHIFT) & PARALLELL_MASK);
+ return (Uint8)((requestInfo >> PARALLEL_SHIFT) & PARALLEL_MASK);
}
inline
@@ -211,58 +213,65 @@ ScanTabReq::clearRequestInfo(UintR & requestInfo){
inline
void
ScanTabReq::setParallelism(UintR & requestInfo, Uint32 type){
- ASSERT_MAX(type, PARALLELL_MASK, "ScanTabReq::setParallellism");
- requestInfo |= (type << PARALLELL_SHIFT);
+ ASSERT_MAX(type, PARALLEL_MASK, "ScanTabReq::setParallelism");
+ requestInfo= (requestInfo & ~(PARALLEL_MASK << PARALLEL_SHIFT)) |
+ ((type & PARALLEL_MASK) << PARALLEL_SHIFT);
}
inline
void
ScanTabReq::setLockMode(UintR & requestInfo, Uint32 mode){
ASSERT_MAX(mode, LOCK_MODE_MASK, "ScanTabReq::setLockMode");
- requestInfo |= (mode << LOCK_MODE_SHIFT);
+ requestInfo= (requestInfo & ~(LOCK_MODE_MASK << LOCK_MODE_SHIFT)) |
+ ((mode & LOCK_MODE_MASK) << LOCK_MODE_SHIFT);
}
inline
void
ScanTabReq::setHoldLockFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setHoldLockFlag");
- requestInfo |= (flag << HOLD_LOCK_SHIFT);
+ requestInfo= (requestInfo & ~(HOLD_LOCK_MASK << HOLD_LOCK_SHIFT)) |
+ ((flag & HOLD_LOCK_MASK) << HOLD_LOCK_SHIFT);
}
inline
void
ScanTabReq::setReadCommittedFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setReadCommittedFlag");
- requestInfo |= (flag << READ_COMMITTED_SHIFT);
+ requestInfo= (requestInfo & ~(READ_COMMITTED_MASK << READ_COMMITTED_SHIFT)) |
+ ((flag & READ_COMMITTED_MASK) << READ_COMMITTED_SHIFT);
}
inline
void
ScanTabReq::setRangeScanFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setRangeScanFlag");
- requestInfo |= (flag << RANGE_SCAN_SHIFT);
+ requestInfo= (requestInfo & ~(RANGE_SCAN_MASK << RANGE_SCAN_SHIFT)) |
+ ((flag & RANGE_SCAN_MASK) << RANGE_SCAN_SHIFT);
}
inline
void
ScanTabReq::setDescendingFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setDescendingFlag");
- requestInfo |= (flag << DESCENDING_SHIFT);
+ requestInfo= (requestInfo & ~(DESCENDING_MASK << DESCENDING_SHIFT)) |
+ ((flag & DESCENDING_MASK) << DESCENDING_SHIFT);
}
inline
void
ScanTabReq::setTupScanFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setTupScanFlag");
- requestInfo |= (flag << TUP_SCAN_SHIFT);
+ requestInfo= (requestInfo & ~(TUP_SCAN_MASK << TUP_SCAN_SHIFT)) |
+ ((flag & TUP_SCAN_MASK) << TUP_SCAN_SHIFT);
}
inline
void
ScanTabReq::setScanBatch(Uint32 & requestInfo, Uint32 flag){
ASSERT_MAX(flag, SCAN_BATCH_MASK, "ScanTabReq::setScanBatch");
- requestInfo &= ~(SCAN_BATCH_MASK << SCAN_BATCH_SHIFT);
- requestInfo |= (flag << SCAN_BATCH_SHIFT);
+ requestInfo= (requestInfo & ~(SCAN_BATCH_MASK << SCAN_BATCH_SHIFT)) |
+ ((flag & SCAN_BATCH_MASK) << SCAN_BATCH_SHIFT);
}
inline
@@ -275,33 +284,36 @@ inline
void
ScanTabReq::setKeyinfoFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setKeyinfoFlag");
- requestInfo |= (flag << KEYINFO_SHIFT);
+ requestInfo= (requestInfo & ~(KEYINFO_MASK << KEYINFO_SHIFT)) |
+ ((flag & KEYINFO_MASK) << KEYINFO_SHIFT);
}
inline
Uint8
ScanTabReq::getDistributionKeyFlag(const UintR & requestInfo){
- return (Uint8)((requestInfo >> SCAN_DISTR_KEY_SHIFT) & 1);
+ return (Uint8)((requestInfo >> SCAN_DISTR_KEY_SHIFT) & SCAN_DISTR_KEY_MASK);
}
inline
void
ScanTabReq::setDistributionKeyFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "ScanTabReq::setKeyinfoFlag");
- requestInfo |= (flag << SCAN_DISTR_KEY_SHIFT);
+ requestInfo= (requestInfo & ~(SCAN_DISTR_KEY_MASK << SCAN_DISTR_KEY_SHIFT)) |
+ ((flag & SCAN_DISTR_KEY_MASK) << SCAN_DISTR_KEY_SHIFT);
}
inline
UintR
ScanTabReq::getNoDiskFlag(const UintR & requestInfo){
- return (requestInfo >> SCAN_NODISK_SHIFT) & 1;
+ return (requestInfo >> SCAN_NODISK_SHIFT) & SCAN_NODISK_MASK;
}
inline
void
ScanTabReq::setNoDiskFlag(UintR & requestInfo, Uint32 flag){
ASSERT_BOOL(flag, "TcKeyReq::setNoDiskFlag");
- requestInfo |= (flag << SCAN_NODISK_SHIFT);
+ requestInfo= (requestInfo & ~(SCAN_NODISK_MASK << SCAN_NODISK_SHIFT)) |
+ ((flag & SCAN_NODISK_MASK) << SCAN_NODISK_SHIFT);
}
/**
diff --git a/storage/ndb/include/kernel/signaldata/TupFrag.hpp b/storage/ndb/include/kernel/signaldata/TupFrag.hpp
index cae548aa094..106518dd47b 100644
--- a/storage/ndb/include/kernel/signaldata/TupFrag.hpp
+++ b/storage/ndb/include/kernel/signaldata/TupFrag.hpp
@@ -29,7 +29,7 @@ class TupFragReq {
friend class Dblqh;
friend class Dbtup;
public:
- STATIC_CONST( SignalLength = 17 );
+ STATIC_CONST( SignalLength = 18 );
private:
Uint32 userPtr;
Uint32 userRef;
@@ -48,6 +48,7 @@ private:
Uint32 checksumIndicator;
Uint32 globalCheckpointIdIndicator;
Uint32 tablespaceid;
+ Uint32 forceVarPartFlag;
};
class TupFragConf {
diff --git a/storage/ndb/include/ndbapi/NdbDictionary.hpp b/storage/ndb/include/ndbapi/NdbDictionary.hpp
index 02dcf81a413..58882e139fd 100644
--- a/storage/ndb/include/ndbapi/NdbDictionary.hpp
+++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp
@@ -939,6 +939,14 @@ public:
void setTemporary(bool);
/**
+ * Only table with varpart do support online add column
+ * Add property so that table wo/ varsize column(s) still
+ * allocates varpart-ref, so that later online add column is possible
+ */
+ bool getForceVarPart() const;
+ void setForceVarPart(bool);
+
+ /**
* Check if any of column in bitmaps are disk columns
* returns bitmap of different columns
* bit 0 = atleast 1 pk column is set
diff --git a/storage/ndb/include/ndbapi/NdbOperation.hpp b/storage/ndb/include/ndbapi/NdbOperation.hpp
index 61ea71e7021..0fa2cac0a32 100644
--- a/storage/ndb/include/ndbapi/NdbOperation.hpp
+++ b/storage/ndb/include/ndbapi/NdbOperation.hpp
@@ -949,6 +949,8 @@ protected:
// get table or index key from prepared signals
int getKeyFromTCREQ(Uint32* data, Uint32 & size);
+ virtual void setReadLockMode(LockMode lockMode);
+
/******************************************************************************
* These are the private variables that are defined in the operation objects.
*****************************************************************************/
diff --git a/storage/ndb/include/ndbapi/NdbScanOperation.hpp b/storage/ndb/include/ndbapi/NdbScanOperation.hpp
index 1897a11c809..bc24b782add 100644
--- a/storage/ndb/include/ndbapi/NdbScanOperation.hpp
+++ b/storage/ndb/include/ndbapi/NdbScanOperation.hpp
@@ -214,6 +214,7 @@ protected:
int init(const NdbTableImpl* tab, NdbTransaction*);
int prepareSend(Uint32 TC_ConnectPtr, Uint64 TransactionId);
int doSend(int ProcessorId);
+ virtual void setReadLockMode(LockMode lockMode);
virtual void setErrorCode(int aErrorCode);
virtual void setErrorCodeAbort(int aErrorCode);
diff --git a/storage/ndb/include/ndbapi/NdbTransaction.hpp b/storage/ndb/include/ndbapi/NdbTransaction.hpp
index 07a46199ecc..20c9c709e51 100644
--- a/storage/ndb/include/ndbapi/NdbTransaction.hpp
+++ b/storage/ndb/include/ndbapi/NdbTransaction.hpp
@@ -369,14 +369,16 @@ public:
void executeAsynch(ExecType aTypeOfExec,
NdbAsynchCallback aCallback,
void* anyObject,
- NdbOperation::AbortOption = NdbOperation::DefaultAbortOption);
+ NdbOperation::AbortOption = NdbOperation::DefaultAbortOption,
+ int forceSend= 0);
#ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED
void executeAsynch(::ExecType aTypeOfExec,
NdbAsynchCallback aCallback,
void* anyObject,
- ::AbortOption abortOption= ::DefaultAbortOption)
+ ::AbortOption abortOption= ::DefaultAbortOption,
+ int forceSend= 0)
{ executeAsynch((ExecType)aTypeOfExec, aCallback, anyObject,
- (NdbOperation::AbortOption)abortOption); }
+ (NdbOperation::AbortOption)abortOption, forceSend); }
#endif
#endif
/**
diff --git a/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp b/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp
index 49392b9beeb..dae5527dc5e 100644
--- a/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp
+++ b/storage/ndb/src/common/debugger/signaldata/DictTabInfo.cpp
@@ -26,6 +26,7 @@ DictTabInfo::TableMapping[] = {
DTIMAP(Table, PrimaryTableId, PrimaryTableId),
DTIMAP2(Table, TableLoggedFlag, TableLoggedFlag, 0, 1),
DTIMAP2(Table, TableTemporaryFlag, TableTemporaryFlag, 0, 1),
+ DTIMAP2(Table, ForceVarPartFlag, ForceVarPartFlag, 0, 1),
DTIMAP2(Table, TableKValue, TableKValue, 6, 6),
DTIMAP2(Table, MinLoadFactor, MinLoadFactor, 0, 90),
DTIMAP2(Table, MaxLoadFactor, MaxLoadFactor, 25, 110),
@@ -124,6 +125,7 @@ DictTabInfo::Table::init(){
PrimaryTableId = RNIL;
TableLoggedFlag = 1;
TableTemporaryFlag = 0;
+ ForceVarPartFlag = 0;
NoOfKeyAttr = 0;
NoOfAttributes = 0;
NoOfNullable = 0;
diff --git a/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp
index 0ba5908ddb4..4b2926d4981 100644
--- a/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp
@@ -1320,7 +1320,7 @@ Dbacc::startNext(Signal* signal, OperationrecPtr lastOp)
* We must check if there are many transactions in parallel queue...
*/
OperationrecPtr tmp;
- tmp.i = loPtr.p->nextParallelQue;
+ tmp= loPtr;
while (tmp.i != RNIL)
{
ptrCheckGuard(tmp, coprecsize, operationrec);
@@ -1332,6 +1332,7 @@ Dbacc::startNext(Signal* signal, OperationrecPtr lastOp)
*/
return;
}
+ tmp.i = tmp.p->nextParallelQue;
}
upgrade:
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
index 318dc1aea8e..66cd523f333 100644
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
@@ -18,6 +18,7 @@
#define DBDICT_C
#include "Dbdict.hpp"
+#include "diskpage.hpp"
#include <ndb_limits.h>
#include <NdbOut.hpp>
@@ -448,6 +449,8 @@ Dbdict::packTableIntoPages(SimpleProperties::Writer & w,
!!(tablePtr.p->m_bits & TableRecord::TR_RowChecksum));
w.add(DictTabInfo::TableTemporaryFlag,
!!(tablePtr.p->m_bits & TableRecord::TR_Temporary));
+ w.add(DictTabInfo::ForceVarPartFlag,
+ !!(tablePtr.p->m_bits & TableRecord::TR_ForceVarPart));
w.add(DictTabInfo::MinLoadFactor, tablePtr.p->minLoadFactor);
w.add(DictTabInfo::MaxLoadFactor, tablePtr.p->maxLoadFactor);
@@ -2819,8 +2822,10 @@ void Dbdict::checkSchemaStatus(Signal* signal)
continue;
case SchemaFile::ADD_STARTED: jam();
case SchemaFile::DROP_TABLE_STARTED: jam();
- ndbrequire(false);
- return;
+ ndbrequire(DictTabInfo::isTable(newEntry->m_tableType) ||
+ DictTabInfo::isIndex(newEntry->m_tableType));
+ newEntry->m_tableState = SchemaFile::INIT;
+ continue;
case SchemaFile::TABLE_ADD_COMMITTED: jam();
case SchemaFile::ALTER_TABLE_COMMITTED: jam();
if (DictTabInfo::isIndex(newEntry->m_tableType) ||
@@ -5411,8 +5416,8 @@ Dbdict::execADD_FRAGREQ(Signal* signal) {
req->tableType = tabPtr.p->tableType;
req->primaryTableId = tabPtr.p->primaryTableId;
req->tablespace_id= tabPtr.p->m_tablespace_id;
- //req->tablespace_id= tablespace_id;
req->logPartId = logPart;
+ req->forceVarPartFlag = !!(tabPtr.p->m_bits& TableRecord::TR_ForceVarPart);
sendSignal(DBLQH_REF, GSN_LQHFRAGREQ, signal,
LqhFragReq::SignalLength, JBB);
}
@@ -6029,6 +6034,8 @@ void Dbdict::handleTabInfoInit(SimpleProperties::Reader & it,
(c_tableDesc.RowGCIFlag ? TableRecord::TR_RowGCI : 0);
tablePtr.p->m_bits |=
(c_tableDesc.TableTemporaryFlag ? TableRecord::TR_Temporary : 0);
+ tablePtr.p->m_bits |=
+ (c_tableDesc.ForceVarPartFlag ? TableRecord::TR_ForceVarPart : 0);
tablePtr.p->minLoadFactor = c_tableDesc.MinLoadFactor;
tablePtr.p->maxLoadFactor = c_tableDesc.MaxLoadFactor;
tablePtr.p->fragmentType = (DictTabInfo::FragmentType)c_tableDesc.FragmentType;
@@ -15492,7 +15499,10 @@ Dbdict::create_fg_prepare_start(Signal* signal, SchemaOp* op){
}
else if(fg.FilegroupType == DictTabInfo::LogfileGroup)
{
- if(!fg.LF_UndoBufferSize)
+ /**
+ * undo_buffer_size can't be less than 96KB in LGMAN block
+ */
+ if(fg.LF_UndoBufferSize < 3 * File_formats::NDB_PAGE_SIZE)
{
op->m_errorCode = CreateFilegroupRef::InvalidUndoBufferSize;
break;
diff --git a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
index a5a9a83d7e3..e5b918ca270 100644
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
@@ -236,10 +236,11 @@ public:
/* Is the table logged (i.e. data survives system restart) */
enum Bits
{
- TR_Logged = 0x1,
- TR_RowGCI = 0x2,
- TR_RowChecksum = 0x4,
- TR_Temporary = 0x8
+ TR_Logged = 0x1,
+ TR_RowGCI = 0x2,
+ TR_RowChecksum = 0x4,
+ TR_Temporary = 0x8,
+ TR_ForceVarPart = 0x10
};
Uint16 m_bits;
diff --git a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
index 8d7290469ca..ba146fce005 100644
--- a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
+++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp
@@ -459,7 +459,8 @@ public:
Uint32 maxRowsHigh;
Uint32 minRowsLow;
Uint32 minRowsHigh;
- };// Size 128 bytes
+ Uint32 forceVarPartFlag;
+ };
typedef Ptr<AddFragRecord> AddFragRecordPtr;
/* $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ */
diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
index 56d320de68c..817e6830a87 100644
--- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
@@ -1092,6 +1092,7 @@ void Dblqh::execLQHFRAGREQ(Signal* signal)
Uint32 primaryTableId = req->primaryTableId;
Uint32 tablespace= req->tablespace_id;
Uint32 logPart = req->logPartId;
+ Uint32 forceVarPartFlag = req->forceVarPartFlag;
if (signal->getLength() < 20)
{
@@ -1211,6 +1212,7 @@ void Dblqh::execLQHFRAGREQ(Signal* signal)
addfragptr.p->tableType = tableType;
addfragptr.p->primaryTableId = primaryTableId;
addfragptr.p->tablespace_id= tablespace;
+ addfragptr.p->forceVarPartFlag = forceVarPartFlag;
//
addfragptr.p->tupConnectptr = RNIL;
addfragptr.p->tuxConnectptr = RNIL;
@@ -1361,6 +1363,7 @@ Dblqh::sendAddFragReq(Signal* signal)
tupFragReq->checksumIndicator = addfragptr.p->checksumIndicator;
tupFragReq->globalCheckpointIdIndicator = addfragptr.p->GCPIndicator;
tupFragReq->tablespaceid = addfragptr.p->tablespace_id;
+ tupFragReq->forceVarPartFlag = addfragptr.p->forceVarPartFlag;
sendSignal(fragptr.p->tupBlockref, GSN_TUPFRAGREQ,
signal, TupFragReq::SignalLength, JBB);
return;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
index f7757ad2ca7..2e8b6695c13 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
@@ -688,7 +688,7 @@ struct Fragrecord {
Uint32 noOfPagesToGrow;
DLList<Page>::Head emptyPrimPage; // allocated pages (not init)
- DLList<Page>::Head thFreeFirst; // pages with atleast 1 free record
+ DLFifoList<Page>::Head thFreeFirst; // pages with atleast 1 free record
SLList<Page>::Head m_empty_pages; // Empty pages not in logical/physical map
Uint32 m_lcp_scan_op;
@@ -1021,18 +1021,12 @@ ArrayPool<TupTriggerData> c_triggerPool;
Uint8 m_null_words;
Uint8 m_null_offset;
Uint16 m_disk_ref_offset; // In words relative m_data
- union {
- Uint16 m_varpart_offset; // In words relative m_data
- Uint16 m_fix_header_size; // For fix size tuples= total rec size(part)
- };
+ Uint16 m_fix_header_size; // For fix size tuples= total rec size(part)
Uint16 m_max_var_offset; // In bytes relative m_var_data.m_data_ptr
} m_offsets[2];
-
Uint32 get_check_offset(Uint32 mm) const {
- Uint32 cnt= m_attributes[mm].m_no_of_varsize;
- Uint32 off= m_offsets[mm].m_varpart_offset;
- return off - (cnt ? 0 : Tuple_header::HeaderSize);
+ return m_offsets[mm].m_fix_header_size;
}
struct {
@@ -1321,6 +1315,11 @@ typedef Ptr<HostBuffer> HostBufferPtr;
#endif
};
+ struct Disk_part_ref
+ {
+ STATIC_CONST( SZ32 = 2 );
+ };
+
struct Tuple_header
{
union {
@@ -1370,14 +1369,24 @@ typedef Ptr<HostBuffer> HostBufferPtr;
return m_null_bits+tabPtrP->m_offsets[mm].m_null_offset;
}
- Uint32* get_var_part_ptr(const Tablerec* tabPtrP) {
- return m_data + tabPtrP->m_offsets[MM].m_varpart_offset;
+ Var_part_ref* get_var_part_ref_ptr(const Tablerec* tabPtrP) {
+ return (Var_part_ref*)(get_disk_ref_ptr(tabPtrP) + Disk_part_ref::SZ32);
}
- const Uint32* get_var_part_ptr(const Tablerec* tabPtrP) const {
- return m_data + tabPtrP->m_offsets[MM].m_varpart_offset;
+ const Var_part_ref* get_var_part_ref_ptr(const Tablerec* tabPtrP) const {
+ return (Var_part_ref*)(get_disk_ref_ptr(tabPtrP) + Disk_part_ref::SZ32);
}
-
+
+ Uint32* get_end_of_fix_part_ptr(const Tablerec* tabPtrP) {
+ return m_data + tabPtrP->m_offsets[MM].m_fix_header_size -
+ Tuple_header::HeaderSize;
+ }
+
+ const Uint32* get_end_of_fix_part_ptr(const Tablerec* tabPtrP) const {
+ return m_data + tabPtrP->m_offsets[MM].m_fix_header_size -
+ Tuple_header::HeaderSize;
+ }
+
Uint32* get_disk_ref_ptr(const Tablerec* tabPtrP) {
return m_data + tabPtrP->m_offsets[MM].m_disk_ref_offset;
}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp
index 563c113ca3d..59adfbfde89 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupAbort.cpp
@@ -152,8 +152,7 @@ void Dbtup::do_tup_abortreq(Signal* signal, Uint32 flags)
ndbassert(tuple_ptr->m_header_bits & Tuple_header::CHAINED_ROW);
- Var_part_ref *ref =
- (Var_part_ref*)tuple_ptr->get_var_part_ptr(regTabPtr.p);
+ Var_part_ref *ref = tuple_ptr->get_var_part_ref_ptr(regTabPtr.p);
Local_key tmp;
ref->copyout(&tmp);
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
index 2b6f4785fca..883e038b125 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupCommit.cpp
@@ -233,16 +233,22 @@ Dbtup::commit_operation(Signal* signal,
}
else
{
- Var_part_ref *ref= (Var_part_ref*)tuple_ptr->get_var_part_ptr(regTabPtr);
- memcpy(tuple_ptr, copy, 4*(Tuple_header::HeaderSize+fixsize));
-
+ /**
+ * Var_part_ref is only stored in *allocated* tuple
+ * so memcpy from copy, will over write it...
+ * hence subtle copyout/assign...
+ */
Local_key tmp;
+ Var_part_ref *ref= tuple_ptr->get_var_part_ref_ptr(regTabPtr);
ref->copyout(&tmp);
+ memcpy(tuple_ptr, copy, 4*fixsize);
+ ref->assign(&tmp);
+
PagePtr vpagePtr;
Uint32 *dst= get_ptr(&vpagePtr, *ref);
Var_page* vpagePtrP = (Var_page*)vpagePtr.p;
- Uint32 *src= copy->get_var_part_ptr(regTabPtr);
+ Uint32 *src= copy->get_end_of_fix_part_ptr(regTabPtr);
Uint32 sz= ((mm_vars + 1) << 1) + (((Uint16*)src)[mm_vars]);
ndbassert(4*vpagePtrP->get_entry_len(tmp.m_page_idx) >= sz);
memcpy(dst, src, sz);
@@ -255,9 +261,8 @@ Dbtup::commit_operation(Signal* signal,
update_free_page_list(regFragPtr, vpagePtr);
}
- disk_ptr = (Tuple_header*)
- (((Uint32*)copy)+Tuple_header::HeaderSize+fixsize+((sz + 3) >> 2));
- }
+ disk_ptr = (Tuple_header*)(((Uint32*)copy)+fixsize+((sz + 3) >> 2));
+ }
if (regTabPtr->m_no_of_disk_attributes &&
(copy_bits & Tuple_header::DISK_INLINE))
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp
index 708b4e0e8d7..1182ac4ee7d 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupDebug.cpp
@@ -366,7 +366,7 @@ operator<<(NdbOut& out, const Dbtup::Tablerec::Tuple_offsets& off)
out << "[ null_words: " << (Uint32)off.m_null_words
<< " null off: " << (Uint32)off.m_null_offset
<< " disk_off: " << off.m_disk_ref_offset
- << " var_off: " << off.m_varpart_offset
+ << " fixheadsz: " << off.m_fix_header_size
<< " max_var_off: " << off.m_max_var_offset
<< " ]";
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
index 73664ecdbe5..bacecfcf20b 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp
@@ -222,9 +222,6 @@ Dbtup::calculateChecksum(Tuple_header* tuple_ptr,
// includes tupVersion
//printf("%p - ", tuple_ptr);
- if (regTabPtr->m_attributes[MM].m_no_of_varsize)
- rec_size += Tuple_header::HeaderSize;
-
for (i= 0; i < rec_size-2; i++) {
checksum ^= tuple_header[i];
//printf("%.8x ", tuple_header[i]);
@@ -1107,7 +1104,7 @@ Dbtup::prepare_initial_insert(KeyReqStruct *req_struct,
const Uint32 cnt1= regTabPtr->m_attributes[MM].m_no_of_varsize;
const Uint32 cnt2= regTabPtr->m_attributes[DD].m_no_of_varsize;
- Uint32 *ptr= req_struct->m_tuple_ptr->get_var_part_ptr(regTabPtr);
+ Uint32 *ptr= req_struct->m_tuple_ptr->get_end_of_fix_part_ptr(regTabPtr);
if(cnt1)
{
@@ -1129,22 +1126,10 @@ Dbtup::prepare_initial_insert(KeyReqStruct *req_struct,
pos += AttributeDescriptor::getSizeInBytes(tab_descr[*order++].tabDescr);
}
}
- else
- {
- ptr -= Tuple_header::HeaderSize;
- }
req_struct->m_disk_ptr= (Tuple_header*)ptr;
- if(cnt2)
- {
- KeyReqStruct::Var_data *dst= &req_struct->m_var_data[DD];
- ptr=((Tuple_header*)ptr)->m_data+regTabPtr->m_offsets[DD].m_varpart_offset;
- dst->m_data_ptr= (char*)(((Uint16*)ptr)+cnt2+1);
- dst->m_offset_array_ptr= req_struct->var_pos_array + (cnt1 << 1);
- dst->m_var_len_offset= cnt2;
- dst->m_max_var_offset= regTabPtr->m_offsets[DD].m_max_var_offset;
- }
+ ndbrequire(cnt2 == 0);
// Set all null bits
memset(req_struct->m_tuple_ptr->m_null_bits+
@@ -2524,13 +2509,14 @@ Dbtup::expand_tuple(KeyReqStruct* req_struct,
Uint16 dd_tot= tabPtrP->m_no_of_disk_attributes;
Uint16 mm_vars= tabPtrP->m_attributes[MM].m_no_of_varsize;
- Uint32 fix_size= tabPtrP->m_offsets[MM].m_varpart_offset;
+ Uint32 fix_size= tabPtrP->m_offsets[MM].m_fix_header_size;
Uint32 order_desc= tabPtrP->m_real_order_descriptor;
- Uint32 *dst_ptr= ptr->get_var_part_ptr(tabPtrP);
+ Uint32 *dst_ptr= ptr->get_end_of_fix_part_ptr(tabPtrP);
const Uint32 *disk_ref= src->get_disk_ref_ptr(tabPtrP);
- const Uint32 *src_ptr= src->get_var_part_ptr(tabPtrP);
- const Uint32 * desc= (Uint32*)req_struct->attr_descr;
+ const Uint32 *src_ptr= src->get_end_of_fix_part_ptr(tabPtrP);
+ const Var_part_ref* var_ref = src->get_var_part_ref_ptr(tabPtrP);
+ const Uint32 *desc= (Uint32*)req_struct->attr_descr;
const Uint16 *order = (Uint16*)(&tableDescriptor[order_desc]);
order += tabPtrP->m_attributes[MM].m_no_of_fixsize;
@@ -2543,7 +2529,7 @@ Dbtup::expand_tuple(KeyReqStruct* req_struct,
if(bits & Tuple_header::CHAINED_ROW)
{
Ptr<Page> var_page;
- src_data= get_ptr(&var_page, * (Var_part_ref*)src_ptr);
+ src_data= get_ptr(&var_page, *var_ref);
step= 4;
sizes[MM]= (2 + (mm_vars << 1) + ((Uint16*)src_data)[mm_vars] + 3) >> 2;
req_struct->m_varpart_page_ptr = var_page;
@@ -2564,14 +2550,12 @@ Dbtup::expand_tuple(KeyReqStruct* req_struct,
ndbassert((UintPtr(src_ptr) & 3) == 0);
src_ptr = ALIGN_WORD(((char*)src_ptr)+step);
- sizes[MM] += fix_size + Tuple_header::HeaderSize;
- memcpy(ptr, src, 4*(fix_size + Tuple_header::HeaderSize));
+ sizes[MM] += fix_size;
+ memcpy(ptr, src, 4*fix_size);
}
else
{
sizes[MM]= 1;
- dst_ptr -= Tuple_header::HeaderSize;
- src_ptr -= Tuple_header::HeaderSize;
memcpy(ptr, src, 4*fix_size);
}
@@ -2605,20 +2589,7 @@ Dbtup::expand_tuple(KeyReqStruct* req_struct,
ndbassert(! (req_struct->m_disk_ptr->m_header_bits & Tuple_header::FREE));
- if(dd_vars)
- {
- KeyReqStruct::Var_data* dst= &req_struct->m_var_data[DD];
- dst_ptr += tabPtrP->m_offsets[DD].m_varpart_offset;
- src_ptr += tabPtrP->m_offsets[DD].m_varpart_offset;
- order += tabPtrP->m_attributes[DD].m_no_of_fixsize;
-
- dst->m_data_ptr= (char*)(char*)(((Uint16*)dst_ptr)+dd_vars+1);
- dst->m_offset_array_ptr= req_struct->var_pos_array + (mm_vars << 1);
- dst->m_var_len_offset= dd_vars;
- dst->m_max_var_offset= tabPtrP->m_offsets[DD].m_max_var_offset;
-
- expand_var_part(dst, src_ptr, desc, order);
- }
+ ndbrequire(dd_vars == 0);
}
ptr->m_header_bits= (bits & ~(Uint32)(Tuple_header::CHAINED_ROW));
@@ -2634,9 +2605,9 @@ Dbtup::prepare_read(KeyReqStruct* req_struct,
Uint16 dd_tot= tabPtrP->m_no_of_disk_attributes;
Uint16 mm_vars= tabPtrP->m_attributes[MM].m_no_of_varsize;
- const Uint32 *src_ptr= ptr->get_var_part_ptr(tabPtrP);
+ const Uint32 *src_ptr= ptr->get_end_of_fix_part_ptr(tabPtrP);
const Uint32 *disk_ref= ptr->get_disk_ref_ptr(tabPtrP);
-
+ const Var_part_ref* var_ref = ptr->get_var_part_ref_ptr(tabPtrP);
if(mm_vars)
{
const Uint32 *src_data= src_ptr;
@@ -2646,7 +2617,7 @@ Dbtup::prepare_read(KeyReqStruct* req_struct,
#if VM_TRACE
#endif
- src_data= get_ptr(* (Var_part_ref*)src_ptr);
+ src_data= get_ptr(* var_ref);
}
dst->m_data_ptr= (char*)(((Uint16*)src_data)+mm_vars+1);
dst->m_offset_array_ptr= (Uint16*)src_data;
@@ -2656,11 +2627,6 @@ Dbtup::prepare_read(KeyReqStruct* req_struct,
// disk part start after varsize (aligned)
src_ptr = ALIGN_WORD(dst->m_data_ptr + dst->m_max_var_offset);
}
- else
- {
- // disk part if after fixsize part...
- src_ptr -= Tuple_header::HeaderSize;
- }
if(disk && dd_tot)
{
@@ -2682,16 +2648,7 @@ Dbtup::prepare_read(KeyReqStruct* req_struct,
// Fix diskpart
req_struct->m_disk_ptr= (Tuple_header*)src_ptr;
ndbassert(! (req_struct->m_disk_ptr->m_header_bits & Tuple_header::FREE));
- if(dd_vars)
- {
- KeyReqStruct::Var_data* dst= &req_struct->m_var_data[DD];
- src_ptr += tabPtrP->m_offsets[DD].m_varpart_offset;
-
- dst->m_data_ptr= (char*)(char*)(((Uint16*)src_ptr)+dd_vars+1);
- dst->m_offset_array_ptr= (Uint16*)src_ptr;
- dst->m_var_len_offset= 1;
- dst->m_max_var_offset= ((Uint16*)src_ptr)[dd_vars];
- }
+ ndbrequire(dd_vars == 0);
}
}
@@ -2706,10 +2663,11 @@ Dbtup::shrink_tuple(KeyReqStruct* req_struct, Uint32 sizes[2],
Uint16 mm_vars= tabPtrP->m_attributes[MM].m_no_of_varsize;
Uint16 dd_vars= tabPtrP->m_attributes[DD].m_no_of_varsize;
- Uint32 *dst_ptr= ptr->get_var_part_ptr(tabPtrP);
+ Uint32 *dst_ptr= ptr->get_end_of_fix_part_ptr(tabPtrP);
Uint16* src_off_ptr= req_struct->var_pos_array;
- sizes[MM]= sizes[DD]= 0;
+ sizes[MM] = 1;
+ sizes[DD] = 0;
if(mm_vars)
{
Uint16* dst_off_ptr= (Uint16*)dst_ptr;
@@ -2733,25 +2691,14 @@ Dbtup::shrink_tuple(KeyReqStruct* req_struct, Uint32 sizes[2],
dst_ptr = ALIGN_WORD(dst_data_ptr);
}
- else
- {
- sizes[MM] = 1;
- dst_ptr -= Tuple_header::HeaderSize;
- }
if(disk && dd_tot)
{
Uint32 * src_ptr = (Uint32*)req_struct->m_disk_ptr;
req_struct->m_disk_ptr = (Tuple_header*)dst_ptr;
- if (unlikely(dd_vars))
- {
- abort();
- }
- else
- {
- sizes[DD] = tabPtrP->m_offsets[DD].m_fix_header_size;
- memmove(dst_ptr, src_ptr, 4*tabPtrP->m_offsets[DD].m_fix_header_size);
- }
+ ndbrequire(dd_vars == 0);
+ sizes[DD] = tabPtrP->m_offsets[DD].m_fix_header_size;
+ memmove(dst_ptr, src_ptr, 4*tabPtrP->m_offsets[DD].m_fix_header_size);
}
}
@@ -2785,7 +2732,7 @@ Dbtup::validate_page(Tablerec* regTabPtr, Var_page* p)
if(!(idx & Var_page::FREE) && !(idx & Var_page::CHAIN))
{
Tuple_header *ptr= (Tuple_header*)page->get_ptr(i);
- Uint32 *part= ptr->get_var_part_ptr(regTabPtr);
+ Uint32 *part= ptr->get_end_of_fix_part_ptr(regTabPtr);
if(ptr->m_header_bits & Tuple_header::CHAINED_ROW)
{
ndbassert(len == fix_sz + 1);
@@ -2849,8 +2796,7 @@ Dbtup::handle_size_change_after_update(KeyReqStruct* req_struct,
Uint32 bits= org->m_header_bits;
Uint32 copy_bits= req_struct->m_tuple_ptr->m_header_bits;
- Uint32 fix_sz = Tuple_header::HeaderSize +
- regTabPtr->m_offsets[MM].m_fix_header_size;
+ Uint32 fix_sz = regTabPtr->m_offsets[MM].m_fix_header_size;
if(sizes[MM] == sizes[2+MM])
;
@@ -2865,7 +2811,7 @@ Dbtup::handle_size_change_after_update(KeyReqStruct* req_struct,
Ptr<Page> pagePtr = req_struct->m_varpart_page_ptr;
Var_page* pageP= (Var_page*)pagePtr.p;
Uint32 idx, alloc, needed;
- Var_part_ref *refptr = (Var_part_ref*)org->get_var_part_ptr(regTabPtr);
+ Var_part_ref *refptr = org->get_var_part_ref_ptr(regTabPtr);
ndbassert(bits & Tuple_header::CHAINED_ROW);
Local_key ref;
@@ -2914,19 +2860,7 @@ Dbtup::nr_update_gci(Uint32 fragPtrI, const Local_key* key, Uint32 gci)
Local_key tmp = *key;
PagePtr page_ptr;
- int ret;
- if (tablePtr.p->m_attributes[MM].m_no_of_varsize)
- {
- tablePtr.p->m_offsets[MM].m_fix_header_size +=
- Tuple_header::HeaderSize+1;
- ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
- tablePtr.p->m_offsets[MM].m_fix_header_size -=
- Tuple_header::HeaderSize+1;
- }
- else
- {
- ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
- }
+ int ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
if (ret)
return -1;
@@ -2954,19 +2888,9 @@ Dbtup::nr_read_pk(Uint32 fragPtrI,
Local_key tmp = *key;
- int ret;
+
PagePtr page_ptr;
- if (tablePtr.p->m_attributes[MM].m_no_of_varsize)
- {
- const Uint32 XXX = Tuple_header::HeaderSize+Var_part_ref::SZ32;
- tablePtr.p->m_offsets[MM].m_fix_header_size += XXX;
- ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
- tablePtr.p->m_offsets[MM].m_fix_header_size -= XXX;
- }
- else
- {
- ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
- }
+ int ret = alloc_page(tablePtr.p, fragPtr.p, &page_ptr, tmp.m_page_no);
if (ret)
return -1;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp
index 31903ea8c33..900a02cfd72 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupFixAlloc.cpp
@@ -90,8 +90,8 @@ Dbtup::alloc_fix_rec(Fragrecord* const regFragPtr,
pagePtr.p->page_state = ZTH_MM_FREE;
- LocalDLList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
- free_pages.add(pagePtr);
+ LocalDLFifoList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
+ free_pages.addFirst(pagePtr);
} else {
jam();
/* ---------------------------------------------------------------- */
@@ -173,7 +173,7 @@ Dbtup::alloc_tuple_from_page(Fragrecord* const regFragPtr,
/* ARE MAINTAINED EVEN AFTER A SYSTEM CRASH. */
/* ---------------------------------------------------------------- */
ndbrequire(regPagePtr->page_state == ZTH_MM_FREE);
- LocalDLList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
+ LocalDLFifoList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
free_pages.remove((Page*)regPagePtr);
regPagePtr->page_state = ZTH_MM_FULL;
}
@@ -193,10 +193,10 @@ void Dbtup::free_fix_rec(Fragrecord* regFragPtr,
{
jam();
PagePtr pagePtr = { (Page*)regPagePtr, key->m_page_no };
- LocalDLList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
+ LocalDLFifoList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
ndbrequire(regPagePtr->page_state == ZTH_MM_FULL);
regPagePtr->page_state = ZTH_MM_FREE;
- free_pages.add(pagePtr);
+ free_pages.addLast(pagePtr);
}
}//Dbtup::freeTh()
@@ -224,13 +224,13 @@ Dbtup::alloc_page(Tablerec* tabPtrP, Fragrecord* fragPtrP,
c_page_pool.getPtr(pagePtr, getRealpid(fragPtrP, page_no));
LocalDLList<Page> alloc_pages(c_page_pool, fragPtrP->emptyPrimPage);
- LocalDLList<Page> free_pages(c_page_pool, fragPtrP->thFreeFirst);
+ LocalDLFifoList<Page> free_pages(c_page_pool, fragPtrP->thFreeFirst);
if (pagePtr.p->page_state == ZEMPTY_MM)
{
convertThPage((Fix_page*)pagePtr.p, tabPtrP, MM);
pagePtr.p->page_state = ZTH_MM_FREE;
alloc_pages.remove(pagePtr);
- free_pages.add(pagePtr);
+ free_pages.addFirst(pagePtr);
}
*ret = pagePtr;
@@ -254,7 +254,7 @@ Dbtup::alloc_fix_rowid(Fragrecord* regFragPtr,
}
Uint32 state = pagePtr.p->page_state;
- LocalDLList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
+ LocalDLFifoList<Page> free_pages(c_page_pool, regFragPtr->thFreeFirst);
switch(state){
case ZTH_MM_FREE:
if (((Fix_page*)pagePtr.p)->alloc_record(idx) != idx)
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp
index 17ab0573376..0427f1c7612 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp
@@ -476,8 +476,7 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI)
ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec);
const Uint32 firstTupleNo = 0;
- const Uint32 tupheadsize = tablePtr.p->m_offsets[MM].m_fix_header_size +
- (buildPtr.p->m_build_vs? Tuple_header::HeaderSize + Var_part_ref::SZ32: 0);
+ const Uint32 tupheadsize = tablePtr.p->m_offsets[MM].m_fix_header_size;
#ifdef TIME_MEASUREMENT
MicroSecondTimer start;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
index cf700c5fa58..9fdfccc8845 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
@@ -175,12 +175,12 @@ void Dbtup::execTUPFRAGREQ(Signal* signal)
regTabPtr.p->m_offsets[MM].m_disk_ref_offset= 0;
regTabPtr.p->m_offsets[MM].m_null_words= 0;
- regTabPtr.p->m_offsets[MM].m_varpart_offset= 0;
+ regTabPtr.p->m_offsets[MM].m_fix_header_size= 0;
regTabPtr.p->m_offsets[MM].m_max_var_offset= 0;
regTabPtr.p->m_offsets[DD].m_disk_ref_offset= 0;
regTabPtr.p->m_offsets[DD].m_null_words= 0;
- regTabPtr.p->m_offsets[DD].m_varpart_offset= 0;
+ regTabPtr.p->m_offsets[DD].m_fix_header_size= 0;
regTabPtr.p->m_offsets[DD].m_max_var_offset= 0;
regTabPtr.p->m_attributes[MM].m_no_of_fixsize= 0;
@@ -463,7 +463,19 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal)
if(regTabPtr.p->m_no_of_disk_attributes > 0)
{
regTabPtr.p->m_offsets[MM].m_disk_ref_offset= pos[MM];
- pos[MM] += 2; // 8 bytes
+ pos[MM] += Disk_part_ref::SZ32; // 8 bytes
+ }
+ else
+ {
+ /**
+ * var part ref is stored at m_disk_ref_offset + Disk_part_ref::SZ32
+ */
+ regTabPtr.p->m_offsets[MM].m_disk_ref_offset= pos[MM]-Disk_part_ref::SZ32;
+ }
+
+ if (regTabPtr.p->m_attributes[MM].m_no_of_varsize)
+ {
+ pos[MM] += Var_part_ref::SZ32;
}
regTabPtr.p->m_offsets[MM].m_null_offset= pos[MM];
@@ -489,16 +501,14 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal)
}
regTabPtr.p->m_offsets[MM].m_fix_header_size=
+ Tuple_header::HeaderSize +
fragOperPtr.p->m_fix_attributes_size[MM] +
pos[MM];
-
+
regTabPtr.p->m_offsets[DD].m_fix_header_size=
fragOperPtr.p->m_fix_attributes_size[DD] +
pos[DD];
- if(regTabPtr.p->m_attributes[MM].m_no_of_varsize == 0)
- regTabPtr.p->m_offsets[MM].m_fix_header_size += Tuple_header::HeaderSize;
-
if(regTabPtr.p->m_attributes[DD].m_no_of_varsize == 0 &&
regTabPtr.p->m_attributes[DD].m_no_of_fixsize > 0)
regTabPtr.p->m_offsets[DD].m_fix_header_size += Tuple_header::HeaderSize;
@@ -538,8 +548,6 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal)
{
Uint32 fix_tupheader = regTabPtr.p->m_offsets[MM].m_fix_header_size;
- if(regTabPtr.p->m_attributes[MM].m_no_of_varsize != 0)
- fix_tupheader += Tuple_header::HeaderSize + Var_part_ref::SZ32;
ndbassert(fix_tupheader > 0);
Uint32 noRowsPerPage = ZWORDS_ON_PAGE / fix_tupheader;
Uint32 noAllocatedPages =
@@ -591,8 +599,12 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal)
Uint32 sz= sizeof(Disk_undo::Create) >> 2;
Logfile_client lgman(this, c_lgman, regFragPtr.p->m_logfile_group_id);
- (void) c_lgman->alloc_log_space(regFragPtr.p->m_logfile_group_id,
- sz);
+ if((terrorCode =
+ c_lgman->alloc_log_space(regFragPtr.p->m_logfile_group_id, sz)))
+ {
+ addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
+ return;
+ }
int res= lgman.get_log_buffer(signal, sz, &cb);
switch(res){
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp
index 380826d9330..6ef8d3585e9 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupPageMap.cpp
@@ -293,7 +293,7 @@ void Dbtup::releaseFragPages(Fragrecord* regFragPtr)
}
{
- LocalDLList<Page> tmp(c_page_pool, regFragPtr->thFreeFirst);
+ LocalDLFifoList<Page> tmp(c_page_pool, regFragPtr->thFreeFirst);
tmp.remove();
}
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
index 107633b3adf..56bac8868b8 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp
@@ -600,8 +600,7 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr)
const bool lcp = (bits & ScanOp::SCAN_LCP);
Uint32 lcp_list = fragPtr.p->m_lcp_keep_list;
- Uint32 size = table.m_offsets[mm].m_fix_header_size +
- (bits&ScanOp::SCAN_VS ? Tuple_header::HeaderSize + Var_part_ref::SZ32 : 0);
+ Uint32 size = table.m_offsets[mm].m_fix_header_size;
if (lcp && lcp_list != RNIL)
goto found_lcp_keep;
diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp
index 0b47ae1faef..1929901f86e 100644
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupVarAlloc.cpp
@@ -68,27 +68,22 @@ Uint32* Dbtup::alloc_var_rec(Fragrecord* fragPtr,
/**
* TODO alloc fix+var part
*/
- const Uint32 XXX = Tuple_header::HeaderSize + Var_part_ref::SZ32;
- tabPtr->m_offsets[MM].m_fix_header_size += XXX;
Uint32 *ptr = alloc_fix_rec(fragPtr, tabPtr, key, out_frag_page_id);
- tabPtr->m_offsets[MM].m_fix_header_size -= XXX;
if (unlikely(ptr == 0))
{
return 0;
}
- ndbassert(alloc_size >= tabPtr->m_offsets[MM].m_fix_header_size +
- Tuple_header::HeaderSize);
+ ndbassert(alloc_size >= tabPtr->m_offsets[MM].m_fix_header_size);
- alloc_size -= tabPtr->m_offsets[MM].m_fix_header_size +
- Tuple_header::HeaderSize;
+ alloc_size -= tabPtr->m_offsets[MM].m_fix_header_size;
Local_key varref;
if (likely(alloc_var_part(fragPtr, tabPtr, alloc_size, &varref) != 0))
{
Tuple_header* tuple = (Tuple_header*)ptr;
- Var_part_ref* dst = (Var_part_ref*)tuple->get_var_part_ptr(tabPtr);
+ Var_part_ref* dst = tuple->get_var_part_ref_ptr(tabPtr);
dst->assign(&varref);
return ptr;
}
@@ -166,7 +161,7 @@ void Dbtup::free_var_rec(Fragrecord* fragPtr,
Tuple_header* tuple = (Tuple_header*)ptr;
Local_key ref;
- Var_part_ref * varref = (Var_part_ref*)tuple->get_var_part_ptr(tabPtr);
+ Var_part_ref * varref = tuple->get_var_part_ref_ptr(tabPtr);
varref->copyout(&ref);
free_fix_rec(fragPtr, tabPtr, key, (Fix_page*)pagePtr.p);
@@ -398,26 +393,21 @@ Dbtup::alloc_var_rowid(Fragrecord* fragPtr,
Local_key* key,
Uint32 * out_frag_page_id)
{
- const Uint32 XXX = Tuple_header::HeaderSize + Var_part_ref::SZ32;
- tabPtr->m_offsets[MM].m_fix_header_size += XXX;
Uint32 *ptr = alloc_fix_rowid(fragPtr, tabPtr, key, out_frag_page_id);
- tabPtr->m_offsets[MM].m_fix_header_size -= XXX;
if (unlikely(ptr == 0))
{
return 0;
}
- ndbassert(alloc_size >= tabPtr->m_offsets[MM].m_fix_header_size +
- Tuple_header::HeaderSize);
+ ndbassert(alloc_size >= tabPtr->m_offsets[MM].m_fix_header_size);
- alloc_size -= tabPtr->m_offsets[MM].m_fix_header_size +
- Tuple_header::HeaderSize;
+ alloc_size -= tabPtr->m_offsets[MM].m_fix_header_size;
Local_key varref;
if (likely(alloc_var_part(fragPtr, tabPtr, alloc_size, &varref) != 0))
{
Tuple_header* tuple = (Tuple_header*)ptr;
- Var_part_ref* dst = (Var_part_ref*)tuple->get_var_part_ptr(tabPtr);
+ Var_part_ref* dst = (Var_part_ref*)tuple->get_var_part_ref_ptr(tabPtr);
dst->assign(&varref);
return ptr;
}
diff --git a/storage/ndb/src/kernel/vm/DLFifoList.hpp b/storage/ndb/src/kernel/vm/DLFifoList.hpp
index a71cb71e207..da92390dd8f 100644
--- a/storage/ndb/src/kernel/vm/DLFifoList.hpp
+++ b/storage/ndb/src/kernel/vm/DLFifoList.hpp
@@ -62,8 +62,9 @@ public:
*/
void insert(Ptr<T> & ptr, Ptr<T>& loc);
+ void remove();
void remove(Ptr<T> &);
-
+ void remove(T*);
/**
* Update i & p value according to <b>i</b>
*/
@@ -277,9 +278,25 @@ DLFifoListImpl<P,T,U>::insert(Ptr<T> & ptr, Ptr<T> & loc)
template <typename P, typename T, typename U>
inline
void
+DLFifoListImpl<P,T,U>::remove()
+{
+ head.firstItem = RNIL;
+ head.lastItem = RNIL;
+}
+
+template <typename P, typename T, typename U>
+inline
+void
DLFifoListImpl<P,T,U>::remove(Ptr<T> & p)
{
- T * t = p.p;
+ remove(p.p);
+}
+
+template <typename P, typename T, typename U>
+inline
+void
+DLFifoListImpl<P,T,U>::remove(T * t)
+{
Uint32 ni = t->U::nextList;
Uint32 pi = t->U::prevList;
@@ -328,7 +345,7 @@ inline
void
DLFifoListImpl<P,T,U>::release(Ptr<T> & p)
{
- remove(p);
+ remove(p.p);
thePool.release(p);
}
diff --git a/storage/ndb/src/mgmapi/mgmapi.cpp b/storage/ndb/src/mgmapi/mgmapi.cpp
index b1a555872eb..e7dc1d1d503 100644
--- a/storage/ndb/src/mgmapi/mgmapi.cpp
+++ b/storage/ndb/src/mgmapi/mgmapi.cpp
@@ -394,7 +394,7 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow<ParserDummy> *command_reply,
}
out.println("");
- CHECK_TIMEDOUT_RET(handle, in, out, NULL);
+ DBUG_CHECK_TIMEDOUT_RET(handle, in, out, NULL);
Parser_t::Context ctx;
ParserDummy session(handle->socket);
@@ -403,7 +403,7 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow<ParserDummy> *command_reply,
const Properties* p = parser.parse(ctx, session);
if (p == NULL){
if(!ndb_mgm_is_connected(handle)) {
- CHECK_TIMEDOUT_RET(handle, in, out, NULL);
+ DBUG_CHECK_TIMEDOUT_RET(handle, in, out, NULL);
DBUG_RETURN(NULL);
}
else
@@ -413,7 +413,7 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow<ParserDummy> *command_reply,
|| ctx.m_status==Parser_t::NoLine)
{
ndb_mgm_disconnect(handle);
- CHECK_TIMEDOUT_RET(handle, in, out, NULL);
+ DBUG_CHECK_TIMEDOUT_RET(handle, in, out, NULL);
DBUG_RETURN(NULL);
}
/**
@@ -438,7 +438,7 @@ ndb_mgm_call(NdbMgmHandle handle, const ParserRow<ParserDummy> *command_reply,
if(p && (in.timedout() || out.timedout()))
delete p;
- CHECK_TIMEDOUT_RET(handle, in, out, NULL);
+ DBUG_CHECK_TIMEDOUT_RET(handle, in, out, NULL);
DBUG_RETURN(p);
}
diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
index c6a3f804b70..dde6829c82c 100644
--- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp
@@ -2160,12 +2160,18 @@ MgmtSrvr::alloc_node_id_req(NodeId free_node_id, enum ndb_mgm_node_type type)
const AllocNodeIdRef * const ref =
CAST_CONSTPTR(AllocNodeIdRef, signal->getDataPtr());
if (ref->errorCode == AllocNodeIdRef::NotMaster ||
- ref->errorCode == AllocNodeIdRef::Busy)
+ ref->errorCode == AllocNodeIdRef::Busy ||
+ ref->errorCode == AllocNodeIdRef::NodeFailureHandlingNotCompleted)
{
do_send = 1;
nodeId = refToNode(ref->masterRef);
if (!theFacade->get_node_alive(nodeId))
nodeId = 0;
+ if (ref->errorCode != AllocNodeIdRef::NotMaster)
+ {
+ /* sleep for a while (100ms) before retrying */
+ NdbSleep_MilliSleep(100);
+ }
continue;
}
return ref->errorCode;
diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp
index b3664107cce..25dcafdef53 100644
--- a/storage/ndb/src/ndbapi/NdbBlob.cpp
+++ b/storage/ndb/src/ndbapi/NdbBlob.cpp
@@ -1390,7 +1390,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl
if (isReadOp()) {
// upgrade lock mode
if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead)
- theNdbOp->theLockMode = NdbOperation::LM_Read;
+ theNdbOp->setReadLockMode(NdbOperation::LM_Read);
// add read of head+inline in this op
if (getHeadInlineValue(theNdbOp) == -1)
DBUG_RETURN(-1);
@@ -1411,7 +1411,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl
if (isScanOp()) {
// upgrade lock mode
if (theNdbOp->theLockMode == NdbOperation::LM_CommittedRead)
- theNdbOp->theLockMode = NdbOperation::LM_Read;
+ theNdbOp->setReadLockMode(NdbOperation::LM_Read);
// add read of head+inline in this op
if (getHeadInlineValue(theNdbOp) == -1)
DBUG_RETURN(-1);
diff --git a/storage/ndb/src/ndbapi/NdbDictionary.cpp b/storage/ndb/src/ndbapi/NdbDictionary.cpp
index b3ee41358d9..a4395fc4b9c 100644
--- a/storage/ndb/src/ndbapi/NdbDictionary.cpp
+++ b/storage/ndb/src/ndbapi/NdbDictionary.cpp
@@ -727,6 +727,16 @@ NdbDictionary::Table::getRowGCIIndicator() const {
return m_impl.m_row_gci;
}
+void
+NdbDictionary::Table::setForceVarPart(bool val){
+ m_impl.m_force_var_part = val;
+}
+
+bool
+NdbDictionary::Table::getForceVarPart() const {
+ return m_impl.m_force_var_part;
+}
+
int
NdbDictionary::Table::aggregate(NdbError& error)
{
diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
index 41b1dbc06e4..63d36bf012f 100644
--- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
+++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
@@ -464,6 +464,7 @@ NdbTableImpl::init(){
m_temporary = false;
m_row_gci = true;
m_row_checksum = true;
+ m_force_var_part = false;
m_kvalue= 6;
m_minLoadFactor= 78;
m_maxLoadFactor= 80;
@@ -752,6 +753,7 @@ NdbTableImpl::assign(const NdbTableImpl& org)
m_temporary = org.m_temporary;
m_row_gci = org.m_row_gci;
m_row_checksum = org.m_row_checksum;
+ m_force_var_part = org.m_force_var_part;
m_kvalue = org.m_kvalue;
m_minLoadFactor = org.m_minLoadFactor;
m_maxLoadFactor = org.m_maxLoadFactor;
@@ -2179,6 +2181,7 @@ NdbDictInterface::parseTableInfo(NdbTableImpl ** ret,
impl->m_temporary = tableDesc->TableTemporaryFlag;
impl->m_row_gci = tableDesc->RowGCIFlag;
impl->m_row_checksum = tableDesc->RowChecksumFlag;
+ impl->m_force_var_part = tableDesc->ForceVarPartFlag;
impl->m_kvalue = tableDesc->TableKValue;
impl->m_minLoadFactor = tableDesc->MinLoadFactor;
impl->m_maxLoadFactor = tableDesc->MaxLoadFactor;
@@ -2693,6 +2696,7 @@ NdbDictInterface::createOrAlterTable(Ndb & ndb,
tmpTab->DefaultNoPartFlag = impl.m_default_no_part_flag;
tmpTab->LinearHashFlag = impl.m_linear_flag;
tmpTab->SingleUserMode = impl.m_single_user_mode;
+ tmpTab->ForceVarPartFlag = impl.m_force_var_part;
if (impl.m_ts_name.length())
{
diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp
index c904b9d65c3..673587b1ed7 100644
--- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp
+++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp
@@ -199,6 +199,7 @@ public:
bool m_temporary;
bool m_row_gci;
bool m_row_checksum;
+ bool m_force_var_part;
int m_kvalue;
int m_minLoadFactor;
int m_maxLoadFactor;
diff --git a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
index f818564642b..ced9b18bd55 100644
--- a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
+++ b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp
@@ -333,6 +333,36 @@ NdbOperation::interpretedDeleteTuple()
}//if
}//NdbOperation::interpretedDeleteTuple()
+void
+NdbOperation::setReadLockMode(LockMode lockMode)
+{
+ /* We only support changing lock mode for read operations at this time. */
+ assert(theOperationType == ReadRequest || theOperationType == ReadExclusive);
+ switch (lockMode)
+ {
+ case LM_CommittedRead:
+ theOperationType= ReadRequest;
+ theSimpleIndicator= 1;
+ theDirtyIndicator= 1;
+ break;
+ case LM_Read:
+ theNdbCon->theSimpleState= 0;
+ theOperationType= ReadRequest;
+ theSimpleIndicator= 0;
+ theDirtyIndicator= 0;
+ break;
+ case LM_Exclusive:
+ theNdbCon->theSimpleState= 0;
+ theOperationType= ReadExclusive;
+ theSimpleIndicator= 0;
+ theDirtyIndicator= 0;
+ break;
+ default:
+ /* Not supported / invalid. */
+ assert(false);
+ }
+ theLockMode= lockMode;
+}
/******************************************************************************
diff --git a/storage/ndb/src/ndbapi/NdbScanOperation.cpp b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
index c253dca8c61..dc9a74ae11c 100644
--- a/storage/ndb/src/ndbapi/NdbScanOperation.cpp
+++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp
@@ -138,34 +138,9 @@ NdbScanOperation::readTuples(NdbScanOperation::LockMode lm,
}
theNdbCon->theScanningOp = this;
- theLockMode = lm;
-
- bool lockExcl, lockHoldMode, readCommitted;
- switch(lm){
- case NdbScanOperation::LM_Read:
- lockExcl = false;
- lockHoldMode = true;
- readCommitted = false;
- break;
- case NdbScanOperation::LM_Exclusive:
- lockExcl = true;
- lockHoldMode = true;
- readCommitted = false;
- break;
- case NdbScanOperation::LM_CommittedRead:
- lockExcl = false;
- lockHoldMode = false;
- readCommitted = true;
- break;
- default:
- setErrorCode(4003);
- return -1;
- }
-
- m_keyInfo = ((scan_flags & SF_KeyInfo) || lockExcl) ? 1 : 0;
bool tupScan = (scan_flags & SF_TupScan);
-#if 1 // XXX temp for testing
+#if 0 // XXX temp for testing
{ char* p = getenv("NDB_USE_TUPSCAN");
if (p != 0) {
unsigned n = atoi(p); // 0-10
@@ -225,13 +200,13 @@ NdbScanOperation::readTuples(NdbScanOperation::LockMode lm,
Uint32 reqInfo = 0;
ScanTabReq::setParallelism(reqInfo, parallel);
ScanTabReq::setScanBatch(reqInfo, 0);
- ScanTabReq::setLockMode(reqInfo, lockExcl);
- ScanTabReq::setHoldLockFlag(reqInfo, lockHoldMode);
- ScanTabReq::setReadCommittedFlag(reqInfo, readCommitted);
ScanTabReq::setRangeScanFlag(reqInfo, rangeScan);
ScanTabReq::setTupScanFlag(reqInfo, tupScan);
req->requestInfo = reqInfo;
+ m_keyInfo = (scan_flags & SF_KeyInfo) ? 1 : 0;
+ setReadLockMode(lm);
+
Uint64 transId = theNdbCon->getTransactionId();
req->transId1 = (Uint32) transId;
req->transId2 = (Uint32) (transId >> 32);
@@ -251,6 +226,41 @@ NdbScanOperation::readTuples(NdbScanOperation::LockMode lm,
return 0;
}
+void
+NdbScanOperation::setReadLockMode(LockMode lockMode)
+{
+ bool lockExcl, lockHoldMode, readCommitted;
+ switch (lockMode)
+ {
+ case LM_CommittedRead:
+ lockExcl= false;
+ lockHoldMode= false;
+ readCommitted= true;
+ break;
+ case LM_Read:
+ lockExcl= false;
+ lockHoldMode= true;
+ readCommitted= false;
+ break;
+ case LM_Exclusive:
+ lockExcl= true;
+ lockHoldMode= true;
+ readCommitted= false;
+ m_keyInfo= 1;
+ break;
+ default:
+ /* Not supported / invalid. */
+ assert(false);
+ }
+ theLockMode= lockMode;
+ ScanTabReq *req= CAST_PTR(ScanTabReq, theSCAN_TABREQ->getDataPtrSend());
+ Uint32 reqInfo= req->requestInfo;
+ ScanTabReq::setLockMode(reqInfo, lockExcl);
+ ScanTabReq::setHoldLockFlag(reqInfo, lockHoldMode);
+ ScanTabReq::setReadCommittedFlag(reqInfo, readCommitted);
+ req->requestInfo= reqInfo;
+}
+
int
NdbScanOperation::fix_receivers(Uint32 parallel){
assert(parallel > 0);
diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp
index a90c658c49b..ada0372a184 100644
--- a/storage/ndb/src/ndbapi/NdbTransaction.cpp
+++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp
@@ -676,6 +676,17 @@ NdbTransaction::executeAsynchPrepare(NdbTransaction::ExecType aTypeOfExec,
DBUG_VOID_RETURN;
}//NdbTransaction::executeAsynchPrepare()
+void
+NdbTransaction::executeAsynch(ExecType aTypeOfExec,
+ NdbAsynchCallback aCallback,
+ void* anyObject,
+ NdbOperation::AbortOption abortOption,
+ int forceSend)
+{
+ executeAsynchPrepare(aTypeOfExec, aCallback, anyObject, abortOption);
+ theNdb->sendPreparedTransactions(forceSend);
+}
+
void NdbTransaction::close()
{
theNdb->closeTransaction(this);
diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c
index df9778b1c73..3cecd15684d 100644
--- a/storage/ndb/src/ndbapi/ndberror.c
+++ b/storage/ndb/src/ndbapi/ndberror.c
@@ -471,7 +471,7 @@ ErrorBundle ErrorCodes[] = {
{ 1700, DMEC, IE, "Undefined error" },
{ 1701, DMEC, AE, "Node already reserved" },
{ 1702, DMEC, AE, "Node already connected" },
- { 1703, DMEC, AE, "Node failure handling not completed" },
+ { 1703, DMEC, IT, "Node failure handling not completed" },
{ 1704, DMEC, AE, "Node type mismatch" },
/**
diff --git a/storage/ndb/test/include/HugoOperations.hpp b/storage/ndb/test/include/HugoOperations.hpp
index a0766af4b50..91e593a2b26 100644
--- a/storage/ndb/test/include/HugoOperations.hpp
+++ b/storage/ndb/test/include/HugoOperations.hpp
@@ -27,7 +27,8 @@ public:
const NdbDictionary::Index* idx = 0);
~HugoOperations();
- int startTransaction(Ndb*);
+ int startTransaction(Ndb*, const NdbDictionary::Table *table= 0,
+ const char *keyData= 0, Uint32 keyLen= 0);
int setTransaction(NdbTransaction*,bool not_null_ok= false);
int closeTransaction(Ndb*);
NdbTransaction* getTransaction();
diff --git a/storage/ndb/test/include/NdbRestarter.hpp b/storage/ndb/test/include/NdbRestarter.hpp
index 916848adf45..9780c0cd1ae 100644
--- a/storage/ndb/test/include/NdbRestarter.hpp
+++ b/storage/ndb/test/include/NdbRestarter.hpp
@@ -27,15 +27,34 @@ public:
int getDbNodeId(int _i);
+ enum RestartFlags {
+ NRRF_INITIAL = 0x1,
+ NRRF_NOSTART = 0x2,
+ NRRF_ABORT = 0x4
+ };
+
int restartOneDbNode(int _nodeId,
bool initial = false,
bool nostart = false,
bool abort = false);
+ int restartOneDbNode2(int _nodeId, Uint32 flags){
+ return restartOneDbNode(_nodeId,
+ flags & NRRF_INITIAL,
+ flags & NRRF_NOSTART,
+ flags & NRRF_ABORT);
+ }
+
int restartAll(bool initial = false,
bool nostart = false,
bool abort = false);
+ int restartAll2(Uint32 flags){
+ return restartAll(flags & NRRF_INITIAL,
+ flags & NRRF_NOSTART,
+ flags & NRRF_ABORT);
+ }
+
int startAll();
int startNodes(const int * _nodes, int _num_nodes);
int waitClusterStarted(unsigned int _timeout = 120);
diff --git a/storage/ndb/test/ndbapi/bank/Bank.hpp b/storage/ndb/test/ndbapi/bank/Bank.hpp
index 312c80ce411..ef6e6976092 100644
--- a/storage/ndb/test/ndbapi/bank/Bank.hpp
+++ b/storage/ndb/test/ndbapi/bank/Bank.hpp
@@ -28,7 +28,7 @@ public:
Bank(Ndb_cluster_connection&, bool init = true, const char *dbase="BANK");
- int setSkipCreate(bool skip) { m_skip_create = skip; }
+ void setSkipCreate(bool skip) { m_skip_create = skip; }
int createAndLoadBank(bool overWrite, bool disk= false, int num_accounts=10);
int dropBank();
diff --git a/storage/ndb/test/ndbapi/testBasic.cpp b/storage/ndb/test/ndbapi/testBasic.cpp
index a032e29bc74..44c3d023169 100644
--- a/storage/ndb/test/ndbapi/testBasic.cpp
+++ b/storage/ndb/test/ndbapi/testBasic.cpp
@@ -1374,6 +1374,74 @@ runBug27756(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+int
+runBug28073(NDBT_Context *ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+ const NdbDictionary::Table *table= ctx->getTab();
+ HugoOperations hugoOp1(*table);
+ HugoOperations hugoOp2(*table);
+ Ndb* pNdb = GETNDB(step);
+ int loops = ctx->getNumLoops();
+ bool inserted= false;
+
+ while (loops--)
+ {
+ if (!inserted)
+ {
+ CHECK(hugoOp1.startTransaction(pNdb) == 0);
+ CHECK(hugoOp1.pkInsertRecord(pNdb, 1, 1) == 0);
+ CHECK(hugoOp1.execute_Commit(pNdb) == 0);
+ CHECK(hugoOp1.closeTransaction(pNdb) == 0);
+ inserted= 1;
+ }
+
+ // Use TC hint to hit the same node in both transactions.
+ Uint32 key_val= 0;
+ const char *key= (const char *)(&key_val);
+ CHECK(hugoOp1.startTransaction(pNdb, table, key, 4) == 0);
+ CHECK(hugoOp2.startTransaction(pNdb, table, key, 4) == 0);
+
+ // First take 2*read lock on the tuple in transaction 1.
+ for (Uint32 i= 0; i < 2; i++)
+ {
+ CHECK(hugoOp1.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ CHECK(hugoOp1.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ }
+ CHECK(hugoOp1.execute_NoCommit(pNdb) == 0);
+
+ // Now send ops in two transactions, one batch.
+ // First 2*read in transaction 2.
+ for (Uint32 i= 0; i < 2; i++)
+ {
+ CHECK(hugoOp2.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ CHECK(hugoOp2.pkReadRecord(pNdb, 1, 1, NdbOperation::LM_Read) == 0);
+ }
+ CHECK(hugoOp2.execute_async_prepare(pNdb, NdbTransaction::NoCommit) == 0);
+
+ // Second op an update in transaction 1.
+ CHECK(hugoOp1.pkUpdateRecord(pNdb, 1, 1) == 0);
+ CHECK(hugoOp1.execute_async_prepare(pNdb, NdbTransaction::Commit) == 0);
+
+ // Transaction 1 will now hang waiting on transaction 2 to commit before it
+ // can upgrade its read lock to a write lock.
+ // With the bug, we get a node failure due to watchdog timeout here.
+ CHECK(hugoOp2.wait_async(pNdb) == 0);
+
+ // Now commit transaction 2, we should see transaction 1 finish with the
+ // update.
+ CHECK(hugoOp2.execute_async_prepare(pNdb, NdbTransaction::Commit) == 0);
+ CHECK(hugoOp2.wait_async(pNdb) == 0);
+ // No error check, as transaction 1 may have terminated already.
+ hugoOp1.wait_async(pNdb);
+
+ CHECK(hugoOp1.closeTransaction(pNdb) == 0);
+ CHECK(hugoOp2.closeTransaction(pNdb) == 0);
+ }
+
+ return result;
+}
+
template class Vector<Uint64>;
NDBT_TESTSUITE(testBasic);
@@ -1656,6 +1724,10 @@ TESTCASE("Bug27756",
"Verify what happens when we fill the db" ){
STEP(runBug27756);
}
+TESTCASE("Bug28073",
+ "Infinite loop in lock queue" ){
+ STEP(runBug28073);
+}
NDBT_TESTSUITE_END(testBasic);
#if 0
diff --git a/storage/ndb/test/ndbapi/testBlobs.cpp b/storage/ndb/test/ndbapi/testBlobs.cpp
index e86714c4fcb..d9c657a0a29 100644
--- a/storage/ndb/test/ndbapi/testBlobs.cpp
+++ b/storage/ndb/test/ndbapi/testBlobs.cpp
@@ -141,6 +141,7 @@ printusage()
<< "bug tests" << endl
<< " -bug 4088 ndb api hang with mixed ops on index table" << endl
<< " -bug 27018 middle partial part write clobbers rest of part" << endl
+ << " -bug 27370 Potential inconsistent blob reads for ReadCommitted reads" << endl
;
}
@@ -1886,12 +1887,210 @@ bugtest_27018()
return 0;
}
+
+struct bug27370_data {
+ Ndb *m_ndb;
+ char m_current_write_value;
+ char *m_writebuf;
+ Uint32 m_blob1_size;
+ Uint32 m_pk1;
+ char m_pk2[g_max_pk2len + 1];
+ bool m_thread_stop;
+};
+
+void *bugtest_27370_thread(void *arg)
+{
+ bug27370_data *data= (bug27370_data *)arg;
+
+ while (!data->m_thread_stop)
+ {
+ memset(data->m_writebuf, data->m_current_write_value, data->m_blob1_size);
+ data->m_current_write_value++;
+
+ NdbConnection *con;
+ if ((con= data->m_ndb->startTransaction()) == 0)
+ return (void *)"Failed to create transaction";
+ NdbOperation *opr;
+ if ((opr= con->getNdbOperation(g_opt.m_tname)) == 0)
+ return (void *)"Failed to create operation";
+ if (opr->writeTuple() != 0)
+ return (void *)"writeTuple() failed";
+ if (opr->equal("PK1", data->m_pk1) != 0)
+ return (void *)"equal(PK1) failed";
+ if (g_opt.m_pk2len != 0)
+ if (opr->equal("PK2", data->m_pk2) != 0)
+ return (void *)"equal(PK2) failed";
+ NdbBlob *bh;
+ if ((bh= opr->getBlobHandle("BL1")) == 0)
+ return (void *)"getBlobHandle() failed";
+ if (bh->setValue(data->m_writebuf, data->m_blob1_size) != 0)
+ return (void *)"setValue() failed";
+ if (con->execute(Commit, AbortOnError, 1) != 0)
+ return (void *)"execute() failed";
+ data->m_ndb->closeTransaction(con);
+ }
+
+ return NULL; // Success
+}
+
+static int
+bugtest_27370()
+{
+ DBG("bug test 27370 - Potential inconsistent blob reads for ReadCommitted reads");
+
+ bug27370_data data;
+
+ data.m_ndb= new Ndb(g_ncc, "TEST_DB");
+ CHK(data.m_ndb->init(20) == 0);
+ CHK(data.m_ndb->waitUntilReady() == 0);
+
+ data.m_current_write_value= 0;
+ data.m_blob1_size= g_opt.m_blob1.m_inline + 10 * g_opt.m_blob1.m_partsize;
+ CHK((data.m_writebuf= new char [data.m_blob1_size]) != 0);
+ data.m_pk1= 27370;
+ memset(data.m_pk2, 'x', g_max_pk2len);
+ data.m_pk2[g_max_pk2len]= '\0';
+ data.m_thread_stop= false;
+
+ memset(data.m_writebuf, data.m_current_write_value, data.m_blob1_size);
+ data.m_current_write_value++;
+
+ CHK((g_con= g_ndb->startTransaction()) != 0);
+ CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->writeTuple() == 0);
+ CHK(g_opr->equal("PK1", data.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", data.m_pk2) == 0);
+ CHK((g_bh1= g_opr->getBlobHandle("BL1")) != 0);
+ CHK(g_bh1->setValue(data.m_writebuf, data.m_blob1_size) == 0);
+ CHK(g_con->execute(Commit) == 0);
+ g_ndb->closeTransaction(g_con);
+ g_con= NULL;
+
+ pthread_t thread_handle;
+ CHK(pthread_create(&thread_handle, NULL, bugtest_27370_thread, &data) == 0);
+
+ DBG("bug test 27370 - PK blob reads");
+ Uint32 seen_updates= 0;
+ while (seen_updates < 50)
+ {
+ CHK((g_con= g_ndb->startTransaction()) != 0);
+ CHK((g_opr= g_con->getNdbOperation(g_opt.m_tname)) != 0);
+ CHK(g_opr->readTuple(NdbOperation::LM_CommittedRead) == 0);
+ CHK(g_opr->equal("PK1", data.m_pk1) == 0);
+ if (g_opt.m_pk2len != 0)
+ CHK(g_opr->equal("PK2", data.m_pk2) == 0);
+ CHK((g_bh1= g_opr->getBlobHandle("BL1")) != 0);
+ CHK(g_con->execute(NoCommit, AbortOnError, 1) == 0);
+
+ const Uint32 loop_max= 10;
+ char read_char;
+ char original_read_char= 0;
+ Uint32 readloop;
+ for (readloop= 0;; readloop++)
+ {
+ if (readloop > 0)
+ {
+ if (readloop > 1)
+ {
+ /* Compare against first read. */
+ CHK(read_char == original_read_char);
+ }
+ else
+ {
+ /*
+ We count the number of times we see the other thread had the
+ chance to update, so that we can be sure it had the opportunity
+ to run a reasonable number of times before we stop.
+ */
+ if (original_read_char != read_char)
+ seen_updates++;
+ original_read_char= read_char;
+ }
+ }
+ if (readloop > loop_max)
+ break;
+ Uint32 readSize= 1;
+ CHK(g_bh1->setPos(urandom(data.m_blob1_size)) == 0);
+ CHK(g_bh1->readData(&read_char, readSize) == 0);
+ CHK(readSize == 1);
+ ExecType commitType= readloop == loop_max ? Commit : NoCommit;
+ CHK(g_con->execute(commitType, AbortOnError, 1) == 0);
+ }
+ g_ndb->closeTransaction(g_con);
+ g_con= NULL;
+ }
+
+ DBG("bug test 27370 - table scan blob reads");
+ seen_updates= 0;
+ while (seen_updates < 50)
+ {
+ CHK((g_con= g_ndb->startTransaction()) != 0);
+ CHK((g_ops= g_con->getNdbScanOperation(g_opt.m_tname)) != 0);
+ CHK(g_ops->readTuples(NdbOperation::LM_CommittedRead) == 0);
+ CHK((g_bh1= g_ops->getBlobHandle("BL1")) != 0);
+ CHK(g_con->execute(NoCommit, AbortOnError, 1) == 0);
+ CHK(g_ops->nextResult(true) == 0);
+
+ const Uint32 loop_max= 10;
+ char read_char;
+ char original_read_char= 0;
+ Uint32 readloop;
+ for (readloop= 0;; readloop++)
+ {
+ if (readloop > 0)
+ {
+ if (readloop > 1)
+ {
+ /* Compare against first read. */
+ CHK(read_char == original_read_char);
+ }
+ else
+ {
+ /*
+ We count the number of times we see the other thread had the
+ chance to update, so that we can be sure it had the opportunity
+ to run a reasonable number of times before we stop.
+ */
+ if (original_read_char != read_char)
+ seen_updates++;
+ original_read_char= read_char;
+ }
+ }
+ if (readloop > loop_max)
+ break;
+ Uint32 readSize= 1;
+ CHK(g_bh1->setPos(urandom(data.m_blob1_size)) == 0);
+ CHK(g_bh1->readData(&read_char, readSize) == 0);
+ CHK(readSize == 1);
+ CHK(g_con->execute(NoCommit, AbortOnError, 1) == 0);
+ }
+
+ CHK(g_ops->nextResult(true) == 1);
+ g_ndb->closeTransaction(g_con);
+ g_con= NULL;
+ }
+
+ data.m_thread_stop= true;
+ void *thread_return;
+ CHK(pthread_join(thread_handle, &thread_return) == 0);
+ DBG("bug 27370 - thread return status: " <<
+ (thread_return ? (char *)thread_return : "<null>"));
+ CHK(thread_return == 0);
+
+ g_con= NULL;
+ g_opr= NULL;
+ g_bh1= NULL;
+ return 0;
+}
+
static struct {
int m_bug;
int (*m_test)();
} g_bugtest[] = {
{ 4088, bugtest_4088 },
- { 27018, bugtest_27018 }
+ { 27018, bugtest_27018 },
+ { 27370, bugtest_27370 }
};
NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)
diff --git a/storage/ndb/test/ndbapi/testNdbApi.cpp b/storage/ndb/test/ndbapi/testNdbApi.cpp
index a7d3c3d0792..f731dc3601f 100644
--- a/storage/ndb/test/ndbapi/testNdbApi.cpp
+++ b/storage/ndb/test/ndbapi/testNdbApi.cpp
@@ -1517,6 +1517,75 @@ runTestIgnoreError(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+static void
+testExecuteAsynchCallback(int res, NdbTransaction *con, void *data_ptr)
+{
+ int *res_ptr= (int *)data_ptr;
+
+ *res_ptr= res;
+}
+
+int runTestExecuteAsynch(NDBT_Context* ctx, NDBT_Step* step){
+ /* Test that NdbTransaction::executeAsynch() works (BUG#27495). */
+ int result = NDBT_OK;
+ const NdbDictionary::Table* pTab = ctx->getTab();
+
+ Ndb* pNdb = new Ndb(&ctx->m_cluster_connection, "TEST_DB");
+ if (pNdb == NULL){
+ ndbout << "pNdb == NULL" << endl;
+ return NDBT_FAILED;
+ }
+ if (pNdb->init(2048)){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbConnection* pCon = pNdb->startTransaction();
+ if (pCon == NULL){
+ ERR(pNdb->getNdbError());
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ NdbScanOperation* pOp = pCon->getNdbScanOperation(pTab->getName());
+ if (pOp == NULL){
+ ERR(pOp->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->readTuples() != 0){
+ ERR(pOp->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+
+ if (pOp->getValue(NdbDictionary::Column::FRAGMENT) == 0){
+ ERR(pOp->getNdbError());
+ pNdb->closeTransaction(pCon);
+ delete pNdb;
+ return NDBT_FAILED;
+ }
+ int res= 42;
+ pCon->executeAsynch(NoCommit, testExecuteAsynchCallback, &res);
+ while(pNdb->pollNdb(100000) == 0)
+ ;
+ if (res != 0){
+ ERR(pCon->getNdbError());
+ ndbout << "Error returned from execute: " << res << endl;
+ result= NDBT_FAILED;
+ }
+
+ pNdb->closeTransaction(pCon);
+
+ delete pNdb;
+
+ return result;
+}
+
template class Vector<NdbScanOperation*>;
@@ -1616,6 +1685,10 @@ TESTCASE("IgnoreError", ""){
FINALIZER(runClearTable);
FINALIZER(createPkIndex_Drop);
}
+TESTCASE("ExecuteAsynch",
+ "Check that executeAsync() works (BUG#27495)\n"){
+ INITIALIZER(runTestExecuteAsynch);
+}
NDBT_TESTSUITE_END(testNdbApi);
int main(int argc, const char** argv){
diff --git a/storage/ndb/test/ndbapi/testNodeRestart.cpp b/storage/ndb/test/ndbapi/testNodeRestart.cpp
index e5e346f3e42..85dbc2aab2a 100644
--- a/storage/ndb/test/ndbapi/testNodeRestart.cpp
+++ b/storage/ndb/test/ndbapi/testNodeRestart.cpp
@@ -1567,6 +1567,72 @@ runBug27466(NDBT_Context* ctx, NDBT_Step* step)
return NDBT_OK;
}
+int
+runBug28023(NDBT_Context* ctx, NDBT_Step* step)
+{
+ int result = NDBT_OK;
+ int loops = ctx->getNumLoops();
+ int records = ctx->getNumRecords();
+ Ndb* pNdb = GETNDB(step);
+ NdbRestarter res;
+
+ if (res.getNumDbNodes() < 2)
+ {
+ return NDBT_OK;
+ }
+
+
+ HugoTransactions hugoTrans(*ctx->getTab());
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.clearTable(pNdb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ for (Uint32 i = 0; i<loops; i++)
+ {
+ int node1 = res.getDbNodeId(rand() % res.getNumDbNodes());
+
+ if (res.restartOneDbNode2(node1,
+ NdbRestarter::NRRF_ABORT |
+ NdbRestarter::NRRF_NOSTART))
+ return NDBT_FAILED;
+
+ if (res.waitNodesNoStart(&node1, 1))
+ return NDBT_FAILED;
+
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.clearTable(pNdb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+
+ res.startNodes(&node1, 1);
+ if (res.waitClusterStarted())
+ return NDBT_FAILED;
+
+ if (hugoTrans.loadTable(pNdb, records) != 0){
+ return NDBT_FAILED;
+ }
+
+ if (hugoTrans.scanUpdateRecords(pNdb, records) != 0)
+ return NDBT_FAILED;
+
+ if (hugoTrans.clearTable(pNdb, records) != 0)
+ {
+ return NDBT_FAILED;
+ }
+ }
+
+ return NDBT_OK;
+}
+
NDBT_TESTSUITE(testNodeRestart);
TESTCASE("NoLoad",
"Test that one node at a time can be stopped and then restarted "\
@@ -1924,6 +1990,9 @@ TESTCASE("Bug27283", ""){
TESTCASE("Bug27466", ""){
INITIALIZER(runBug27466);
}
+TESTCASE("Bug28023", ""){
+ INITIALIZER(runBug28023);
+}
NDBT_TESTSUITE_END(testNodeRestart);
int main(int argc, const char** argv){
diff --git a/storage/ndb/test/run-test/daily-basic-tests.txt b/storage/ndb/test/run-test/daily-basic-tests.txt
index df4f6c1ab38..96473c28199 100644
--- a/storage/ndb/test/run-test/daily-basic-tests.txt
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt
@@ -244,6 +244,10 @@ cmd: testBasic
args: -n Bug27756
max-time: 500
+cmd: testBasic
+args: -n Bug28073
+
+max-time: 500
cmd: testIndex
args: -n Bug25059 -r 3000 T1
@@ -549,6 +553,10 @@ max-time: 1000
cmd: testNodeRestart
args: -n Bug26481 T1
+max-time: 1000
+cmd: testNodeRestart
+args: -n Bug28023 T7 D2
+
#
# DICT TESTS
max-time: 1500
@@ -684,6 +692,10 @@ max-time: 500
cmd: testNdbApi
args: -n Bug_WritePartialIgnoreError T1
+max-time: 500
+cmd: testNdbApi
+args: -n ExecuteAsynch T1
+
#max-time: 500
#cmd: testInterpreter
#args: T1
@@ -704,6 +716,14 @@ max-time: 1500
cmd: testBlobs
args:
+max-time: 600
+cmd: testBlobs
+args: -bug 27018
+
+max-time: 600
+cmd: testBlobs
+args: -bug 27370
+
max-time: 5000
cmd: testOIBasic
args: -case abcdefz
diff --git a/storage/ndb/test/src/HugoOperations.cpp b/storage/ndb/test/src/HugoOperations.cpp
index 481ce0c0567..1a2e5180f1f 100644
--- a/storage/ndb/test/src/HugoOperations.cpp
+++ b/storage/ndb/test/src/HugoOperations.cpp
@@ -15,13 +15,15 @@
#include <HugoOperations.hpp>
-int HugoOperations::startTransaction(Ndb* pNdb){
+int HugoOperations::startTransaction(Ndb* pNdb,
+ const NdbDictionary::Table *table,
+ const char *keyData, Uint32 keyLen){
if (pTrans != NULL){
ndbout << "HugoOperations::startTransaction, pTrans != NULL" << endl;
return NDBT_FAILED;
}
- pTrans = pNdb->startTransaction();
+ pTrans = pNdb->startTransaction(table, keyData, keyLen);
if (pTrans == NULL) {
const NdbError err = pNdb->getNdbError();
ERR(err);
diff --git a/storage/ndb/test/src/HugoTransactions.cpp b/storage/ndb/test/src/HugoTransactions.cpp
index f0f042d306b..456782f4726 100644
--- a/storage/ndb/test/src/HugoTransactions.cpp
+++ b/storage/ndb/test/src/HugoTransactions.cpp
@@ -1146,7 +1146,7 @@ HugoTransactions::pkInterpretedUpdateRecords(Ndb* pNdb,
}
// PKs
- if (equalForRow(pOp, r) != 0)
+ if (equalForRow(pUpdOp, r) != 0)
{
closeTransaction(pNdb);
return NDBT_FAILED;
@@ -1714,7 +1714,7 @@ HugoTransactions::indexUpdateRecords(Ndb* pNdb,
if(!ordered)
{
- if (equalForRow(pOp, r+b) != 0)
+ if (equalForRow(pUpdOp, r+b) != 0)
{
closeTransaction(pNdb);
return NDBT_FAILED;
diff --git a/storage/ndb/test/src/NDBT_Table.cpp b/storage/ndb/test/src/NDBT_Table.cpp
index 2e0e5939266..1787bef9aba 100644
--- a/storage/ndb/test/src/NDBT_Table.cpp
+++ b/storage/ndb/test/src/NDBT_Table.cpp
@@ -34,6 +34,7 @@ operator <<(class NdbOut& ndbout, const NDBT_Table & tab)
ndbout << "Row Checksum: " << tab.getRowChecksumIndicator() << endl;
ndbout << "Row GCI: " << tab.getRowGCIIndicator() << endl;
ndbout << "SingleUserMode: " << (Uint32) tab.getSingleUserMode() << endl;
+ ndbout << "ForceVarPart: " << tab.getForceVarPart() << endl;
//<< ((tab.getTupleKey() == TupleId) ? " tupleid" : "") <<endl;
ndbout << "TableStatus: ";
diff --git a/storage/ndb/test/tools/Makefile.am b/storage/ndb/test/tools/Makefile.am
index 25b4e20a682..1683d4d84ae 100644
--- a/storage/ndb/test/tools/Makefile.am
+++ b/storage/ndb/test/tools/Makefile.am
@@ -13,7 +13,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc listen_event eventlog
+ndbtest_PROGRAMS = hugoLoad hugoFill hugoLockRecords hugoPkDelete hugoPkRead hugoPkReadRecord hugoPkUpdate hugoScanRead hugoScanUpdate restart verify_index copy_tab create_index ndb_cpcc listen_event eventlog rep_latency
# transproxy
@@ -34,6 +34,7 @@ create_index_SOURCES = create_index.cpp
ndb_cpcc_SOURCES = cpcc.cpp
listen_event_SOURCES = listen.cpp
eventlog_SOURCES = log_listner.cpp
+rep_latency_SOURCES = rep_latency.cpp
include $(top_srcdir)/storage/ndb/config/common.mk.am
include $(top_srcdir)/storage/ndb/config/type_ndbapitest.mk.am
diff --git a/storage/ndb/test/tools/listen.cpp b/storage/ndb/test/tools/listen.cpp
index fd404ff0134..e51b213195b 100644
--- a/storage/ndb/test/tools/listen.cpp
+++ b/storage/ndb/test/tools/listen.cpp
@@ -22,6 +22,128 @@
#include <getarg.h>
+#define BATCH_SIZE 128
+struct Table_info
+{
+ Uint32 id;
+};
+
+struct Trans_arg
+{
+ Ndb *ndb;
+ NdbTransaction *trans;
+ Uint32 bytes_batched;
+};
+
+Vector< Vector<NdbRecAttr*> > event_values;
+Vector< Vector<NdbRecAttr*> > event_pre_values;
+Vector<struct Table_info> table_infos;
+
+static void do_begin(Ndb *ndb, struct Trans_arg &trans_arg)
+{
+ trans_arg.ndb = ndb;
+ trans_arg.trans = ndb->startTransaction();
+ trans_arg.bytes_batched = 0;
+}
+
+static void do_equal(NdbOperation *op,
+ NdbEventOperation *pOp)
+{
+ struct Table_info *ti = (struct Table_info *)pOp->getCustomData();
+ Vector<NdbRecAttr*> &ev = event_values[ti->id];
+ const NdbDictionary::Table *tab= pOp->getTable();
+ unsigned i, n_columns = tab->getNoOfColumns();
+ for (i= 0; i < n_columns; i++)
+ {
+ if (tab->getColumn(i)->getPrimaryKey() &&
+ op->equal(i, ev[i]->aRef()))
+ {
+ abort();
+ }
+ }
+}
+
+static void do_set_value(NdbOperation *op,
+ NdbEventOperation *pOp)
+{
+ struct Table_info *ti = (struct Table_info *)pOp->getCustomData();
+ Vector<NdbRecAttr*> &ev = event_values[ti->id];
+ const NdbDictionary::Table *tab= pOp->getTable();
+ unsigned i, n_columns = tab->getNoOfColumns();
+ for (i= 0; i < n_columns; i++)
+ {
+ if (!tab->getColumn(i)->getPrimaryKey() &&
+ op->setValue(i, ev[i]->aRef()))
+ {
+ abort();
+ }
+ }
+}
+
+static void do_insert(struct Trans_arg &trans_arg, NdbEventOperation *pOp)
+{
+ if (!trans_arg.trans)
+ return;
+
+ NdbOperation *op =
+ trans_arg.trans->getNdbOperation(pOp->getEvent()->getTableName());
+ op->writeTuple();
+
+ do_equal(op, pOp);
+ do_set_value(op, pOp);
+
+ trans_arg.bytes_batched++;
+ if (trans_arg.bytes_batched > BATCH_SIZE)
+ {
+ trans_arg.trans->execute(NdbTransaction::NoCommit);
+ trans_arg.bytes_batched = 0;
+ }
+}
+static void do_update(struct Trans_arg &trans_arg, NdbEventOperation *pOp)
+{
+ if (!trans_arg.trans)
+ return;
+
+ NdbOperation *op =
+ trans_arg.trans->getNdbOperation(pOp->getEvent()->getTableName());
+ op->writeTuple();
+
+ do_equal(op, pOp);
+ do_set_value(op, pOp);
+
+ trans_arg.bytes_batched++;
+ if (trans_arg.bytes_batched > BATCH_SIZE)
+ {
+ trans_arg.trans->execute(NdbTransaction::NoCommit);
+ trans_arg.bytes_batched = 0;
+ }
+}
+static void do_delete(struct Trans_arg &trans_arg, NdbEventOperation *pOp)
+{
+ if (!trans_arg.trans)
+ return;
+
+ NdbOperation *op =
+ trans_arg.trans->getNdbOperation(pOp->getEvent()->getTableName());
+ op->deleteTuple();
+
+ do_equal(op, pOp);
+
+ trans_arg.bytes_batched++;
+ if (trans_arg.bytes_batched > BATCH_SIZE)
+ {
+ trans_arg.trans->execute(NdbTransaction::NoCommit);
+ trans_arg.bytes_batched = 0;
+ }
+}
+static void do_commit(struct Trans_arg &trans_arg)
+{
+ if (!trans_arg.trans)
+ return;
+ trans_arg.trans->execute(NdbTransaction::Commit);
+ trans_arg.ndb->closeTransaction(trans_arg.trans);
+}
+
int
main(int argc, const char** argv){
ndb_init();
@@ -29,8 +151,14 @@ main(int argc, const char** argv){
int _help = 0;
const char* db = 0;
+ const char* connectstring1 = 0;
+ const char* connectstring2 = 0;
struct getargs args[] = {
+ { "connectstring1", 'c',
+ arg_string, &connectstring1, "connectstring1", "" },
+ { "connectstring2", 'C',
+ arg_string, &connectstring2, "connectstring2", "" },
{ "database", 'd', arg_string, &db, "Database", "" },
{ "usage", '?', arg_flag, &_help, "Print help", "" }
};
@@ -46,7 +174,7 @@ main(int argc, const char** argv){
}
// Connect to Ndb
- Ndb_cluster_connection con;
+ Ndb_cluster_connection con(connectstring1);
if(con.connect(12, 5, 1) != 0)
{
return NDBT_ProgramExit(NDBT_FAILED);
@@ -61,12 +189,35 @@ main(int argc, const char** argv){
// Connect to Ndb and wait for it to become ready
while(MyNdb.waitUntilReady() != 0)
ndbout << "Waiting for ndb to become ready..." << endl;
-
+
+ Ndb_cluster_connection *con2 = NULL;
+ Ndb *ndb2 = NULL;
+ if (connectstring2)
+ {
+ con2 = new Ndb_cluster_connection(connectstring2);
+
+ if(con2->connect(12, 5, 1) != 0)
+ {
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+ ndb2 = new Ndb( con2, db ? db : "TEST_DB" );
+
+ if(ndb2->init() != 0){
+ ERR(ndb2->getNdbError());
+ return NDBT_ProgramExit(NDBT_FAILED);
+ }
+
+ // Connect to Ndb and wait for it to become ready
+ while(ndb2->waitUntilReady() != 0)
+ ndbout << "Waiting for ndb to become ready..." << endl;
+ }
+
int result = 0;
NdbDictionary::Dictionary *myDict = MyNdb.getDictionary();
Vector<NdbDictionary::Event*> events;
Vector<NdbEventOperation*> event_ops;
+ int sz = 0;
for(i= optind; i<argc; i++)
{
const NdbDictionary::Table* table= myDict->getTable(argv[i]);
@@ -121,12 +272,23 @@ main(int argc, const char** argv){
goto end;
}
+ event_values.push_back(Vector<NdbRecAttr *>());
+ event_pre_values.push_back(Vector<NdbRecAttr *>());
for (int a = 0; a < table->getNoOfColumns(); a++)
{
- pOp->getValue(table->getColumn(a)->getName());
- pOp->getPreValue(table->getColumn(a)->getName());
+ event_values[sz].
+ push_back(pOp->getValue(table->getColumn(a)->getName()));
+ event_pre_values[sz].
+ push_back(pOp->getPreValue(table->getColumn(a)->getName()));
}
event_ops.push_back(pOp);
+ {
+ struct Table_info ti;
+ ti.id = sz;
+ table_infos.push_back(ti);
+ }
+ pOp->setCustomData((void *)&table_infos[sz]);
+ sz++;
}
for(i= 0; i<(int)event_ops.size(); i++)
@@ -140,6 +302,7 @@ main(int argc, const char** argv){
}
}
+ struct Trans_arg trans_arg;
while(true)
{
while(MyNdb.pollEvents(100) == 0);
@@ -149,18 +312,26 @@ main(int argc, const char** argv){
{
Uint64 gci= pOp->getGCI();
Uint64 cnt_i= 0, cnt_u= 0, cnt_d= 0;
+ if (ndb2)
+ do_begin(ndb2, trans_arg);
do
{
switch(pOp->getEventType())
{
case NdbDictionary::Event::TE_INSERT:
cnt_i++;
+ if (ndb2)
+ do_insert(trans_arg, pOp);
break;
case NdbDictionary::Event::TE_DELETE:
cnt_d++;
+ if (ndb2)
+ do_delete(trans_arg, pOp);
break;
case NdbDictionary::Event::TE_UPDATE:
cnt_u++;
+ if (ndb2)
+ do_update(trans_arg, pOp);
break;
case NdbDictionary::Event::TE_CLUSTER_FAILURE:
break;
@@ -180,6 +351,8 @@ main(int argc, const char** argv){
abort();
}
} while ((pOp= MyNdb.nextEvent()) && gci == pOp->getGCI());
+ if (ndb2)
+ do_commit(trans_arg);
ndbout_c("GCI: %lld events: %lld(I) %lld(U) %lld(D)", gci, cnt_i, cnt_u, cnt_d);
}
}
@@ -187,8 +360,15 @@ end:
for(i= 0; i<(int)event_ops.size(); i++)
MyNdb.dropEventOperation(event_ops[i]);
+ if (ndb2)
+ delete ndb2;
+ if (con2)
+ delete con2;
return NDBT_ProgramExit(NDBT_OK);
}
+template class Vector<struct Table_info>;
+template class Vector<NdbRecAttr*>;
+template class Vector< Vector<NdbRecAttr*> >;
template class Vector<NdbDictionary::Event*>;
template class Vector<NdbEventOperation*>;
diff --git a/storage/ndb/test/tools/rep_latency.cpp b/storage/ndb/test/tools/rep_latency.cpp
new file mode 100644
index 00000000000..5ca9a1a1190
--- /dev/null
+++ b/storage/ndb/test/tools/rep_latency.cpp
@@ -0,0 +1,304 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ * Update on master wait for update on slave
+ *
+ */
+
+#include <NdbApi.hpp>
+#include <NdbSleep.h>
+#include <sys/time.h>
+#include <NdbOut.hpp>
+#include <NDBT.hpp>
+
+struct Xxx
+{
+ Ndb *ndb;
+ const NdbDictionary::Table *table;
+ Uint32 pk_col;
+ Uint32 col;
+};
+
+struct XxxR
+{
+ Uint32 pk_val;
+ Uint32 val;
+ struct timeval start_time;
+ Uint32 latency;
+};
+
+static int
+prepare_master_or_slave(Ndb &myNdb,
+ const char* table,
+ const char* pk,
+ Uint32 pk_val,
+ const char* col,
+ struct Xxx &xxx,
+ struct XxxR &xxxr);
+static void
+run_master_update(struct Xxx &xxx, struct XxxR &xxxr);
+static void
+run_slave_wait(struct Xxx &xxx, struct XxxR &xxxr);
+
+#define PRINT_ERROR(code,msg) \
+ g_err << "Error in " << __FILE__ << ", line: " << __LINE__ \
+ << ", code: " << code \
+ << ", msg: " << msg << ".\n"
+#define APIERROR(error) { \
+ PRINT_ERROR((error).code, (error).message); \
+ exit(-1); }
+
+int main(int argc, char** argv)
+{
+ if (argc != 8)
+ {
+ ndbout << "Arguments are <connect_string cluster 1> <connect_string cluster 2> <database> <table name> <primary key> <value of primary key> <attribute to update>.\n";
+ exit(-1);
+ }
+ // ndb_init must be called first
+ ndb_init();
+ {
+ const char *opt_connectstring1 = argv[1];
+ const char *opt_connectstring2 = argv[2];
+ const char *opt_db = argv[3];
+ const char *opt_table = argv[4];
+ const char *opt_pk = argv[5];
+ const Uint32 opt_pk_val = atoi(argv[6]);
+ const char *opt_col = argv[7];
+
+ // Object representing the cluster 1
+ Ndb_cluster_connection cluster1_connection(opt_connectstring1);
+ // Object representing the cluster 2
+ Ndb_cluster_connection cluster2_connection(opt_connectstring2);
+
+ // connect cluster 1 and run application
+ // Connect to cluster 1 management server (ndb_mgmd)
+ if (cluster1_connection.connect(4 /* retries */,
+ 5 /* delay between retries */,
+ 1 /* verbose */))
+ {
+ g_err << "Cluster 1 management server was not ready within 30 secs.\n";
+ exit(-1);
+ }
+ // Optionally connect and wait for the storage nodes (ndbd's)
+ if (cluster1_connection.wait_until_ready(30,0) < 0)
+ {
+ g_err << "Cluster 1 was not ready within 30 secs.\n";
+ exit(-1);
+ }
+ // connect cluster 2 and run application
+ // Connect to cluster management server (ndb_mgmd)
+ if (cluster2_connection.connect(4 /* retries */,
+ 5 /* delay between retries */,
+ 1 /* verbose */))
+ {
+ g_err << "Cluster 2 management server was not ready within 30 secs.\n";
+ exit(-1);
+ }
+ // Optionally connect and wait for the storage nodes (ndbd's)
+ if (cluster2_connection.wait_until_ready(30,0) < 0)
+ {
+ g_err << "Cluster 2 was not ready within 30 secs.\n";
+ exit(-1);
+ }
+ // Object representing the database
+ Ndb myNdb1(&cluster1_connection, opt_db);
+ Ndb myNdb2(&cluster2_connection, opt_db);
+ //
+ struct Xxx xxx1;
+ struct Xxx xxx2;
+ struct XxxR xxxr;
+ prepare_master_or_slave(myNdb1, opt_table, opt_pk, opt_pk_val, opt_col,
+ xxx1, xxxr);
+ prepare_master_or_slave(myNdb2, opt_table, opt_pk, opt_pk_val, opt_col,
+ xxx2, xxxr);
+ while (1)
+ {
+ // run the application code
+ run_master_update(xxx1, xxxr);
+ run_slave_wait(xxx2, xxxr);
+ ndbout << "latency: " << xxxr.latency << endl;
+ }
+ }
+ // Note: all connections must have been destroyed before calling ndb_end()
+ ndb_end(0);
+
+ return 0;
+}
+
+static int
+prepare_master_or_slave(Ndb &myNdb,
+ const char* table,
+ const char* pk,
+ Uint32 pk_val,
+ const char* col,
+ struct Xxx &xxx,
+ struct XxxR &xxxr)
+{
+ if (myNdb.init())
+ APIERROR(myNdb.getNdbError());
+ const NdbDictionary::Dictionary* myDict = myNdb.getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable(table);
+ if (myTable == NULL)
+ APIERROR(myDict->getNdbError());
+ const NdbDictionary::Column *myPkCol = myTable->getColumn(pk);
+ if (myPkCol == NULL)
+ APIERROR(myDict->getNdbError());
+ if (myPkCol->getType() != NdbDictionary::Column::Unsigned)
+ {
+ PRINT_ERROR(0, "Primary key column not of type unsigned");
+ exit(-1);
+ }
+ const NdbDictionary::Column *myCol = myTable->getColumn(col);
+ if (myCol == NULL)
+ APIERROR(myDict->getNdbError());
+ if (myCol->getType() != NdbDictionary::Column::Unsigned)
+ {
+ PRINT_ERROR(0, "Update column not of type unsigned");
+ exit(-1);
+ }
+
+ xxx.ndb = &myNdb;
+ xxx.table = myTable;
+ xxx.pk_col = myPkCol->getColumnNo();
+ xxx.col = myCol->getColumnNo();
+
+ xxxr.pk_val = pk_val;
+
+ return 0;
+}
+
+static void run_master_update(struct Xxx &xxx, struct XxxR &xxxr)
+{
+ Ndb *ndb = xxx.ndb;
+ const NdbDictionary::Table *myTable = xxx.table;
+ int retry_sleep= 10; /* 10 milliseconds */
+ int retries= 100;
+ while (1)
+ {
+ Uint32 val;
+ NdbTransaction *trans = ndb->startTransaction();
+ if (trans == NULL)
+ goto err;
+ {
+ NdbOperation *op = trans->getNdbOperation(myTable);
+ if (op == NULL)
+ APIERROR(trans->getNdbError());
+ op->readTupleExclusive();
+ op->equal(xxx.pk_col, xxxr.pk_val);
+ op->getValue(xxx.col, (char *)&val);
+ }
+ if (trans->execute(NdbTransaction::NoCommit))
+ goto err;
+ //fprintf(stderr, "read %u\n", val);
+ xxxr.val = val + 1;
+ {
+ NdbOperation *op = trans->getNdbOperation(myTable);
+ if (op == NULL)
+ APIERROR(trans->getNdbError());
+ op->updateTuple();
+ op->equal(xxx.pk_col, xxxr.pk_val);
+ op->setValue(xxx.col, xxxr.val);
+ }
+ if (trans->execute(NdbTransaction::Commit))
+ goto err;
+ ndb->closeTransaction(trans);
+ //fprintf(stderr, "updated to %u\n", xxxr.val);
+ break;
+err:
+ const NdbError this_error= trans ?
+ trans->getNdbError() : ndb->getNdbError();
+ if (this_error.status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ if (trans)
+ ndb->closeTransaction(trans);
+ NdbSleep_MilliSleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ if (trans)
+ ndb->closeTransaction(trans);
+ APIERROR(this_error);
+ }
+ /* update done start timer */
+ gettimeofday(&xxxr.start_time, 0);
+}
+
+static void run_slave_wait(struct Xxx &xxx, struct XxxR &xxxr)
+{
+ struct timeval old_end_time = xxxr.start_time, end_time;
+ Ndb *ndb = xxx.ndb;
+ const NdbDictionary::Table *myTable = xxx.table;
+ int retry_sleep= 10; /* 10 milliseconds */
+ int retries= 100;
+ while (1)
+ {
+ Uint32 val;
+ NdbTransaction *trans = ndb->startTransaction();
+ if (trans == NULL)
+ goto err;
+ {
+ NdbOperation *op = trans->getNdbOperation(myTable);
+ if (op == NULL)
+ APIERROR(trans->getNdbError());
+ op->readTuple();
+ op->equal(xxx.pk_col, xxxr.pk_val);
+ op->getValue(xxx.col, (char *)&val);
+ if (trans->execute(NdbTransaction::Commit))
+ goto err;
+ }
+ /* read done, check time of read */
+ gettimeofday(&end_time, 0);
+ ndb->closeTransaction(trans);
+ //fprintf(stderr, "read %u waiting for %u\n", val, xxxr.val);
+ if (xxxr.val != val)
+ {
+ /* expected value not received yet */
+ retries = 100;
+ NdbSleep_MilliSleep(retry_sleep);
+ old_end_time = end_time;
+ continue;
+ }
+ break;
+err:
+ const NdbError this_error= trans ?
+ trans->getNdbError() : ndb->getNdbError();
+ if (this_error.status == NdbError::TemporaryError)
+ {
+ if (retries--)
+ {
+ if (trans)
+ ndb->closeTransaction(trans);
+ NdbSleep_MilliSleep(retry_sleep);
+ continue; // retry
+ }
+ }
+ if (trans)
+ ndb->closeTransaction(trans);
+ APIERROR(this_error);
+ }
+
+ Int64 elapsed_usec1 =
+ ((Int64)end_time.tv_sec - (Int64)xxxr.start_time.tv_sec)*1000*1000 +
+ ((Int64)end_time.tv_usec - (Int64)xxxr.start_time.tv_usec);
+ Int64 elapsed_usec2 =
+ ((Int64)end_time.tv_sec - (Int64)old_end_time.tv_sec)*1000*1000 +
+ ((Int64)end_time.tv_usec - (Int64)old_end_time.tv_usec);
+ xxxr.latency =
+ ((elapsed_usec1 - elapsed_usec2/2)+999)/1000;
+}
diff --git a/storage/ndb/tools/Makefile.am b/storage/ndb/tools/Makefile.am
index 7480b9a2ae9..de0f36963bf 100644
--- a/storage/ndb/tools/Makefile.am
+++ b/storage/ndb/tools/Makefile.am
@@ -14,7 +14,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
dist_bin_SCRIPTS = ndb_size.pl ndb_error_reporter
-dist_pkgdata_DATA = ndb_size.tmpl
+dist_pkgdata_DATA =
ndbtools_PROGRAMS = \
ndb_test_platform \
diff --git a/storage/ndb/tools/ndb_size.pl b/storage/ndb/tools/ndb_size.pl
index 3d1ea3f4231..4ecdc297cd3 100644
--- a/storage/ndb/tools/ndb_size.pl
+++ b/storage/ndb/tools/ndb_size.pl
@@ -1,17 +1,29 @@
#!/usr/bin/perl -w
+# Copyright (C) 2005-2007 MySQL AB
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
use strict;
use DBI;
use POSIX;
-use HTML::Template;
+use Getopt::Long;
# MySQL Cluster size estimator
# ----------------------------
#
-# (C)2005 MySQL AB
-#
-#
# The purpose of this tool is to work out storage requirements
# from an existing MySQL database.
#
@@ -25,157 +37,578 @@ use HTML::Template;
#
# BUGS
# ----
-# - enum/set is 0 byte storage! Woah - efficient!
# - DECIMAL is 0 byte storage. A bit too efficient.
# - some float stores come out weird (when there's a comma e.g. 'float(4,1)')
# - no disk data values
# - computes the storage requirements of views (and probably MERGE)
-# - ignores character sets.
+# - ignores character sets?
+
+package MySQL::NDB::Size::Parameter;
+
+use Class::MethodMaker [
+ scalar => 'name',
+ scalar => 'default',
+ scalar => 'mem_per_item',
+ scalar => [{ -default => '' },'unit'],
+ hash => [ qw ( value
+ ver_mem_per_item
+ mem_total ) ],
+ new => [ -hash => 'new' ],
+ ];
+
+1;
+
+package MySQL::NDB::Size::Report;
+
+use Class::MethodMaker [
+ scalar => [ qw( database
+ dsn ) ],
+ array => 'versions',
+ hash => [qw( tables
+ parameters
+ supporting_tables) ],
+ new => [ -hash => 'new' ],
+ ];
+1;
+
+package MySQL::NDB::Size::Column;
-my $template = HTML::Template->new(filename => 'ndb_size.tmpl',
- die_on_bad_params => 0)
- or die "Could not open ndb_size.tmpl.";
+use Class::MethodMaker [
+ new => [ -hash => 'new' ],
+ scalar => [ qw( name
+ type
+ is_varsize
+ size
+ Key) ],
+ hash => 'dm_overhead',
+ scalar => [{ -default => 4 },'align'],
+ scalar => [{ -default => 0 },'null_bits'],
+ ];
-my $dbh;
+# null_bits:
+# 0 if not nullable, 1 if nullable
+# + additional if bitfield as these are stored in the null bits
+# if is_varsize, null_bits are in varsize part.
-if(@ARGV < 3 || $ARGV[0] eq '--usage' || $ARGV[0] eq '--help')
+# dm is default DataMemory value. Automatically 4byte aligned
+# ver_dm is DataMemory value for specific versions.
+# an entry in ver_dm OVERRIDES the dm value.
+# e.g. if way column stored changed in new version.
+#
+# if is_varsize, dm/ver_dm is in varsized part.
+
+sub ver_dm_exists
{
- print STDERR "Usage:\n";
- print STDERR "\tndb_size.pl database hostname user password\n\n";
- print STDERR "If you need to specify a port number, use host:port\n\n";
- exit(1);
+ my ($self,$ver)= @_;
+ return exists($self->{ver_dm}{$ver});
}
+use Data::Dumper;
+sub ver_dm
{
- my $database= $ARGV[0];
- my $hostname= $ARGV[1];
- my $user= $ARGV[2];
- my $password= $ARGV[3];
- my $dsn = "DBI:mysql:database=$database;host=$hostname";
- $dbh= DBI->connect($dsn, $user, $password) or exit(1);
- $template->param(db => $database);
- $template->param(dsn => $dsn);
+ my ($self,$ver,$val)= @_;
+ if(@_ > 2)
+ {
+ $self->{ver_dm}{$ver}=
+ $self->align * POSIX::floor(($val+$self->align-1)/$self->align);
+ }
+ return $self->{ver_dm}{$ver};
}
-my @releases = ({rel=>'4.1'},{rel=>'5.0'},{rel=>'5.1'}); #,{rel=>'5.1-dd'});
-$template->param(releases => \@releases);
+sub dm
+{
+ my ($self,$val)= @_;
+ if(@_ > 1)
+ {
+ $self->{dm}=
+ $self->align * POSIX::floor(($val+$self->align-1)/$self->align)
+ }
+ return $self->{dm};
+}
-my $tables = $dbh->selectall_arrayref("show tables");
+package MySQL::NDB::Size::Index;
-my @table_size;
+use Class::MethodMaker [
+ new => [ -hash => 'new' ],
+ hash => [ qw( ver_dm
+ ver_im ) ],
+ scalar => [ qw( name
+ type
+ comment
+ columns
+ unique
+ dm
+ im) ],
+ scalar => [ { -default=> 4 },'align'],
+ scalar => [ { -default=> 0 },'is_supporting_table' ],
+ ];
-my @dbDataMemory;
-my @dbIndexMemory;
-my @NoOfAttributes;
-my @NoOfIndexes;
-my @NoOfTables;
-$NoOfTables[$_]{val} = @{$tables} foreach 0..$#releases;
+package MySQL::NDB::Size::Table;
+# The following are computed by compute_row_size:
+# row_dm_size DataMemory Size per row
+# row_vdm_size Varsized DataMemory Size per row
+# row_ddm_size Disk Data size per row (on disk size)
+#
+# These are hashes of versions. If an entry in (dm|vdm|ddm)_versions exists,
+# then this parameter is calculated.
+#
+# Specific per-row overhead is in row_(dm|vdm|ddm)_overhead.
+# e.g. if there is a varsized column, we have a vdm overhead for the
+# varsized part of the row, otherwise vdm_size==0
+
+# Any supporting tables - e.g. BLOBs have their name in supporting_tables
+# These tables should then be looked up in the main report object.
+# The main report object also has a supporting_tables hash used for
+# excluding these from the main list of tables.
+use POSIX;
+use Class::MethodMaker [
+ new => [ -hash => 'new' ],
+ array => [ qw( supporting_tables
+ dm_versions
+ vdm_versions
+ ddm_versions ) ],
+ scalar => [ qw( name
+ rows ) ],
+ hash => [ qw( columns
+ indexes
+ indexed_columns
+ row_im_size
+ row_dm_size
+ row_vdm_size
+ row_dm_overhead
+ row_vdm_overhead
+ row_ddm_overhead) ],
+ scalar => [ { -default=> 8192 },'im_pagesize'],
+ scalar => [ { -default=> 0 },'im_page_overhead'],
+ scalar => [ { -default=> 32768 },'dm_pagesize' ],
+ scalar => [ { -default=> 128 },'dm_page_overhead' ],
+ scalar => [ { -default=> 32768 },'vdm_pagesize' ],
+ scalar => [ { -default=> 128 },'vdm_page_overhead' ],
+ hash => [ # these are computed
+ qw(
+ dm_null_bytes
+ vdm_null_bytes
+ dm_needed
+ vdm_needed
+ im_needed
+ im_rows_per_page
+ dm_rows_per_page
+ vdm_rows_per_page) ],
+ scalar => [ { -default=> 4 },'align'],
+ ];
+
+sub compute_row_size
+{
+ my ($self, $releases) = @_;
-sub align {
- my($to,@unaligned) = @_;
- my @aligned;
- foreach my $x (@unaligned) {
- push @aligned, $to * POSIX::floor(($x+$to-1)/$to);
+ my %row_dm_size;
+ my %row_vdm_size;
+ my %row_im_size;
+ my %dm_null_bits;
+ my %vdm_null_bits;
+ my $no_varsize= 0;
+
+ foreach my $c (keys %{$self->columns})
+ {
+ if($self->columns->{$c}->is_varsize)
+ {
+ $no_varsize++;
+ foreach my $ver ($self->vdm_versions)
+ {
+ if($self->columns->{$c}->ver_dm_exists($ver))
+ {
+ $row_vdm_size{$ver}+= $self->columns->{$c}->ver_dm($ver);
+ $vdm_null_bits{$ver}+= $self->columns->{$c}->null_bits();
+ }
+ else
+ {
+ $row_vdm_size{$ver}+= $self->columns->{$c}->dm;
+ $vdm_null_bits{$ver}+= $self->columns->{$c}->null_bits();
+ }
+ }
+ }
+ foreach my $ver ($self->dm_versions)
+ {
+ if($self->columns->{$c}->ver_dm_exists($ver))
+ {
+ next if $self->columns->{$c}->is_varsize;
+ $row_dm_size{$ver}+= $self->columns->{$c}->ver_dm($ver);
+ $dm_null_bits{$ver}+= $self->columns->{$c}->null_bits();
+ }
+ else
+ {
+ $row_dm_size{$ver}+= $self->columns->{$c}->dm||0;
+ $dm_null_bits{$ver}+= $self->columns->{$c}->null_bits()||0;
+ }
+ }
+ }
+
+ foreach ($self->row_dm_overhead_keys())
+ {
+ $row_dm_size{$_}+= $self->row_dm_overhead->{$_}
+ if exists($row_dm_size{$_});
}
- return @aligned;
+
+
+ foreach ($self->row_vdm_overhead_keys())
+ {
+ $row_vdm_size{$_}+= $self->row_vdm_overhead->{$_}
+ if exists($row_vdm_size{$_});
+ }
+
+
+ # now we compute size of indexes for dm
+ foreach my $i (keys %{$self->indexes})
+ {
+ foreach my $ver ($self->dm_versions)
+ {
+ $row_dm_size{$ver}+= $self->indexes->{$i}->dm() || 0;
+ }
+ }
+
+ # now we compute size of indexes for im
+ while(my ($iname, $i) = $self->indexes_each())
+ {
+ foreach my $ver ($self->dm_versions)
+ {
+ if($i->ver_im_exists($ver))
+ {
+ $row_im_size{$ver}+= $i->ver_im->{$ver};
+ }
+ else
+ {
+ $row_im_size{$ver}+= $i->im() || 0;
+ }
+ }
+ }
+
+ # 32-bit align the null part
+ foreach my $k (keys %dm_null_bits)
+ {
+ $dm_null_bits{$k}=
+ $self->align * POSIX::floor((ceil($dm_null_bits{$k}/8)+$self->align-1)
+ /$self->align);
+ }
+
+ foreach my $k (keys %vdm_null_bits)
+ {
+ $vdm_null_bits{$k}=
+ $self->align * POSIX::floor((ceil($vdm_null_bits{$k}/8)+$self->align-1)
+ /$self->align);
+ }
+
+ # Finally set things
+ $self->dm_null_bytes(%dm_null_bits);
+ $self->vdm_null_bytes(%vdm_null_bits);
+
+ # add null bytes to dm/vdm size
+ foreach my $k (keys %row_dm_size)
+ {
+ $row_dm_size{$k}+=$dm_null_bits{$k}||0;
+ }
+
+ foreach my $k (keys %row_vdm_size)
+ {
+ $row_vdm_size{$k}+=$vdm_null_bits{$k}||0;
+ }
+
+ $self->row_dm_size(%row_dm_size);
+ $self->row_vdm_size(%row_vdm_size);
+ $self->row_im_size(%row_im_size);
+}
+
+sub compute_estimate
+{
+ my ($self) = @_;
+
+ foreach my $ver (@{$self->dm_versions})
+ {
+ $self->dm_rows_per_page_set($ver =>
+ floor(
+ ($self->dm_pagesize - $self->dm_page_overhead)
+ /
+ $self->row_dm_size->{$ver}
+ )
+ );
+ }
+
+ foreach my $ver (@{$self->vdm_versions})
+ {
+ next if ! $self->row_vdm_size_exists($ver);
+ $self->vdm_rows_per_page_set($ver =>
+ floor(
+ ($self->vdm_pagesize - $self->vdm_page_overhead)
+ /
+ $self->row_vdm_size->{$ver}
+ )
+ );
+ }
+
+ $self->im_page_overhead(0) if !$self->im_page_overhead();
+ foreach my $ver (@{$self->dm_versions})
+ {
+ $self->im_rows_per_page_set($ver =>
+ floor(
+ ($self->im_pagesize - $self->im_page_overhead)
+ /
+ $self->row_im_size->{$ver}
+ )
+ );
+ }
+
+ $self->dm_needed_set($_ => $self->dm_pagesize()
+ *
+ POSIX::ceil(
+ $self->rows
+ /
+ ($self->dm_rows_per_page->{$_})
+ )
+ )
+ foreach $self->dm_versions;
+
+ $self->vdm_needed_set($_ => (!$self->vdm_rows_per_page->{$_})? 0 :
+ $self->vdm_pagesize()
+ *
+ POSIX::ceil(
+ $self->rows
+ /
+ ($self->vdm_rows_per_page->{$_})
+ )
+ )
+ foreach $self->vdm_versions;
+
+ $self->im_needed_set($_ => $self->im_pagesize()
+ *
+ POSIX::ceil(
+ $self->rows
+ /
+ ($self->im_rows_per_page->{$_})
+ )
+ )
+ foreach $self->dm_versions;
+}
+
+package main;
+
+my ($dbh,$database,$hostname,$user,$password,$help,$savequeries,$loadqueries,$debug,$format);
+
+GetOptions('database|d=s'=>\$database,
+ 'hostname=s'=>\$hostname,
+ 'user|u=s'=>\$user,
+ 'password|p=s'=>\$password,
+ 'savequeries|s=s'=>\$savequeries,
+ 'loadqueries|l=s'=>\$loadqueries,
+ 'help|usage|h!'=>\$help,
+ 'debug'=>\$debug,
+ 'format|f=s'=>\$format,
+ );
+
+my $report= new MySQL::NDB::Size::Report;
+
+if($help || !$database)
+{
+ print STDERR "Usage:\n";
+ print STDERR "\tndb_size.pl --database=<db name> [--hostname=<host>]"
+ ."[--user=<user>] [--password=<password>] [--help|-h] [--format=(html|text)] [--loadqueries=<file>] [--savequeries=<file>]\n\n";
+ print STDERR "\t--hostname=<host>:<port> can be used to designate a "
+ ."specific port\n";
+ print STDERR "\t--hostname defaults to localhost\n";
+ print STDERR "\t--user and --password default to empty string\n";
+ print STDERR "\t--format=(html|text) Output format\n";
+ print STDERR "\t--savequeries=<file> saves all queries to the DB into <file>\n";
+ print STDERR "\t--loadqueries=<file> loads query results from <file>. Doesn't connect to DB.\n";
+ exit(1);
+}
+
+$hostname= 'localhost' unless $hostname;
+
+my %queries; # used for loadqueries/savequeries
+
+if(!$loadqueries)
+{
+ my $dsn = "DBI:mysql:database=$database;host=$hostname";
+ $dbh= DBI->connect($dsn, $user, $password) or exit(1);
+ $report->database($database);
+ $report->dsn($dsn);
+}
+else
+{
+ open Q,"< $loadqueries";
+ my @q= <Q>;
+ my $VAR1;
+ my $e= eval join("",@q) or die $@;
+ %queries= %$e;
+ close Q;
+ $report->database("file:$loadqueries");
+ $report->dsn("file:$loadqueries");
+}
+
+$report->versions('4.1','5.0','5.1');
+
+my $tables;
+
+if($loadqueries)
+{
+ $tables= $queries{"show tables"};
+}
+else
+{
+ $tables= $dbh->selectall_arrayref("show tables");
+ $queries{"show tables"}= $tables;
}
sub do_table {
- my $table= shift;
+ my $t= shift;
my $info= shift;
my %indexes= %{$_[0]};
my @count= @{$_[1]};
- my @columns;
- my %columnsize; # used for index calculations
- # We now work out the DataMemory usage
-
- # sizes for 4.1, 5.0, 5.1 and 5.1-dd
- my @totalsize= (0,0,0,0);
- @totalsize= @totalsize[0..$#releases]; # limit to releases we're outputting
- my $nrvarsize= 0;
+ $t->dm_versions($report->versions);
+ $t->vdm_versions('5.1');
+ $t->ddm_versions('5.1');
- foreach(keys %$info)
+ foreach my $colname (keys %$info)
{
- my @realsize = (0,0,0,0);
- my @varsize = (0,0,0,0);
- my $type;
- my $size;
- my $name= $_;
- my $is_varsize= 0;
+ my $col= new MySQL::NDB::Size::Column(name => $colname);
+ my ($type, $size);
+
+ $col->Key($$info{$colname}{Key})
+ if(defined($$info{$colname}{Key}) &&$$info{$colname}{Key} ne '');
- if($$info{$_}{Type} =~ /^(.*?)\((\d+)\)/)
+ $col->null_bits(defined($$info{$colname}{Null})
+ && $$info{$colname}{Null} eq 'YES');
+
+ if(defined($$info{$colname}{Type})
+ && $$info{$colname}{Type} =~ /^(.*?)\((.+)\)/)
{
$type= $1;
$size= $2;
}
+ elsif(exists($$info{$colname}{type}))
+ {
+ # we have a Column object..
+ $type= $$info{$colname}{type};
+ $size= $$info{$colname}{size};
+ }
else
{
- $type= $$info{$_}{Type};
+ $type= $$info{$colname}{Type};
}
+ $col->type($type);
+ $col->size($size);
if($type =~ /tinyint/)
- {@realsize=(1,1,1,1)}
+ {$col->dm(1)}
elsif($type =~ /smallint/)
- {@realsize=(2,2,2,2)}
+ {$col->dm(2)}
elsif($type =~ /mediumint/)
- {@realsize=(3,3,3,3)}
+ {$col->dm(3)}
elsif($type =~ /bigint/)
- {@realsize=(8,8,8,8)}
+ {$col->dm(8)}
elsif($type =~ /int/)
- {@realsize=(4,4,4,4)}
+ {$col->dm(4)}
elsif($type =~ /float/)
{
- if($size<=24)
- {@realsize=(4,4,4,4)}
+ if(!defined($size) || $size<=24)
+ {$col->dm(4)}
else
- {@realsize=(8,8,8,8)}
+ {$col->dm(8)}
}
elsif($type =~ /double/ || $type =~ /real/)
- {@realsize=(8,8,8,8)}
+ {$col->dm(8)}
elsif($type =~ /bit/)
{
- my $a=($size+7)/8;
- @realsize = ($a,$a,$a,$a);
+ # bitfields stored in null bits
+ $col->null_bits($size+($col->null_bits()||0));
}
elsif($type =~ /datetime/)
- {@realsize=(8,8,8,8)}
+ {$col->dm(8)}
elsif($type =~ /timestamp/)
- {@realsize=(4,4,4,4)}
+ {$col->dm(4)}
elsif($type =~ /date/ || $type =~ /time/)
- {@realsize=(3,3,3,3)}
+ {$col->dm(3)}
elsif($type =~ /year/)
- {@realsize=(1,1,1,1)}
+ {$col->dm(1)}
+ elsif($type =~ /enum/ || $type =~ /set/)
+ {
+ # I *think* this is correct..
+ my @items= split ',',$size;
+ $col->dm(ceil((scalar @items)/256));
+ }
elsif($type =~ /varchar/ || $type =~ /varbinary/)
{
my $fixed=$size+ceil($size/256);
- my @dynamic=$dbh->selectrow_array("select avg(length(`"
- .$name
- ."`)) from `".$table.'`');
- $dynamic[0]=0 if !$dynamic[0];
- $dynamic[0]+=ceil($dynamic[0]/256); # size bit
- $nrvarsize++;
- $is_varsize= 1;
- $varsize[3]= ceil($dynamic[0]);
- @realsize= ($fixed,$fixed,ceil($dynamic[0]),$fixed);
+ $col->dm_overhead_set('length' => ceil($size/256));
+ $col->dm($fixed);
+ if(!$col->Key()) # currently keys must be non varsized
+ {
+ my $sql= "select avg(length(`"
+ .$colname
+ ."`)) from `".$t->name().'`';
+ my @dynamic;
+ if($loadqueries)
+ {
+ @dynamic= @{$queries{$sql}};
+ }
+ else
+ {
+ @dynamic= $dbh->selectrow_array($sql);
+ $queries{$sql}= \@dynamic;
+ }
+ $dynamic[0]=0 if ! defined($dynamic[0]) || !@dynamic;
+ $dynamic[0]+=ceil($size/256); # size bit
+ $col->is_varsize(1);
+ $col->ver_dm('5.1',ceil($dynamic[0]));
+ }
}
elsif($type =~ /binary/ || $type =~ /char/)
- {@realsize=($size,$size,$size,$size)}
+ {$col->dm($size)}
elsif($type =~ /text/ || $type =~ /blob/)
{
- @realsize=(8+256,8+256,8+256,8+256);
+ $col->dm_overhead_set('length' => 8);
+ $col->dm(8+256);
my $blobhunk= 2000;
$blobhunk= 8000 if $type=~ /longblob/;
$blobhunk= 4000 if $type=~ /mediumblob/;
- my @blobsize=$dbh->selectrow_array("select SUM(CEILING(".
- "length(`$name`)/$blobhunk))".
- "from `".$table."`");
+ my $sql= "select SUM(CEILING(".
+ "length(`$colname`)/$blobhunk))"
+ ."from `".$t->name."`";
+ my @blobsize;
+ if($loadqueries)
+ {
+ @blobsize= @{$queries{$sql}};
+ }
+ else
+ {
+ @blobsize= $dbh->selectrow_array($sql);
+ $queries{$sql}= \@blobsize;
+ }
$blobsize[0]=0 if !defined($blobsize[0]);
- #$NoOfTables[$_]{val} += 1 foreach 0..$#releases; # blob uses table
- do_table($table."\$BLOB_$name",
+
+ # Is a supporting table, add it to the lists:
+ $report->supporting_tables_set($t->name()."\$BLOB_$colname" => 1);
+ $t->supporting_tables_push($t->name()."\$BLOB_$colname");
+
+ my $st= new MySQL::NDB::Size::Table(name =>
+ $t->name()."\$BLOB_$colname",
+ rows => $blobsize[0],
+ row_dm_overhead =>
+ { '4.1' => 12,
+ '5.0' => 12,
+ '5.1' => 16,
+ },
+ row_vdm_overhead =>
+ { '5.1' => 8 },
+ row_ddm_overhead =>
+ { '5.1' => 8 },
+ );
+
+
+
+ do_table($st,
{'PK'=>{Type=>'int'},
'DIST'=>{Type=>'int'},
'PART'=>{Type=>'int'},
@@ -195,26 +628,12 @@ sub do_table {
\@blobsize);
}
- @realsize= @realsize[0..$#releases];
- @realsize= align(4,@realsize);
-
- $totalsize[$_]+=$realsize[$_] foreach 0..$#totalsize;
-
- my @realout;
- push @realout,{val=>$_} foreach @realsize;
-
- push @columns, {
- name=>$name,
- type=>$type,
- is_varsize=>$is_varsize,
- size=>$size,
- key=>$$info{$_}{Key},
- datamemory=>\@realout,
- };
-
- $columnsize{$name}= \@realsize; # used for index calculations
+ $col->type($type);
+ $col->size($size);
+ $t->columns_set( $colname => $col );
}
-
+ $report->tables_set( $t->name => $t );
+
# And now... the IndexMemory usage.
#
# Firstly, we assemble some information about the indexes.
@@ -222,170 +641,1058 @@ sub do_table {
# we can still connect to pre-5.0 mysqlds.
if(!defined($indexes{PRIMARY})) {
- my @usage= ({val=>8},{val=>8},{val=>8},{val=>8});
- @usage= @usage[0..$#releases];
- $indexes{PRIMARY}= {
- type=>'BTREE',
- unique=>1,
- comment=>'Hidden pkey created by NDB',
- columns=>['HIDDEN_NDB_PKEY'],
- };
- push @columns, {
- name=>'HIDDEN_NDB_PKEY',
- type=>'bigint',
- size=>8,
- key=>'PRI',
- datamemory=>\@usage,
- };
- $columnsize{'HIDDEN_NDB_PKEY'}= [8,8,8];
- }
+ my $i= new MySQL::NDB::Size::Index(
+ name => 'PRIMARY',
+ unique => 1,
+ comment =>'Hidden pkey created by NDB',
+ type =>'BTREE',
+ columns => ['HIDDEN_NDB_PKEY'],
+ );
- my @IndexDataMemory= ({val=>0},{val=>0},{val=>0},{val=>0});
- my @RowIndexMemory= ({val=>0},{val=>0},{val=>0},{val=>0});
- @IndexDataMemory= @IndexDataMemory[0..$#releases];
- @RowIndexMemory= @RowIndexMemory[0..$#releases];
+ $i->im(16);
+ $i->dm(16);
+ $i->ver_im('4.1',25+8);
- my @indexes;
- foreach my $index (keys %indexes) {
- my $im41= 25;
- $im41+=$columnsize{$_}[0] foreach @{$indexes{$index}{columns}};
- my @im = ({val=>$im41},{val=>25},{val=>25}); #,{val=>25});
- my @dm = ({val=>10},{val=>10},{val=>10}); #,{val=>10});
- push @indexes, {
- name=>$index,
- type=>$indexes{$index}{type},
- columns=>join(',',@{$indexes{$index}{columns}}),
- indexmemory=>\@im,
- datamemory=>\@dm,
- };
- $IndexDataMemory[$_]{val}+=$dm[$_]{val} foreach 0..$#releases;
- $RowIndexMemory[$_]{val}+=$im[$_]{val} foreach 0..$#releases;
+ $t->indexes_set('PRIMARY' => $i);
+ $t->indexed_columns_set('HIDDEN_NDB_PKEY' => 1);
+
+ $t->columns_set('HIDDEN_NDB_PKEY' =>
+ new MySQL::NDB::Size::Column(
+ name => 'HIDDEN_NDB_PKEY',
+ type => 'bigint',
+ dm => 8,
+ Key => 'PRI'));
}
- # total size + 16 bytes overhead
- my @TotalDataMemory;
- my @RowOverhead = ({val=>16},{val=>16},{val=>16}); #,{val=>24});
- # 5.1 has ptr to varsize page, and per-varsize overhead
- my @nrvarsize_mem= ({val=>0},{val=>0},
- {val=>8}); #,{val=>0});
+ my @indexes;
+
+ # We model the PRIMARY first as needed for secondary uniq indexes
+ if(defined($indexes{'PRIMARY'}))
{
- my @a= align(4,$nrvarsize*2);
- $nrvarsize_mem[2]{val}+=$a[0]+$nrvarsize*4;
+ my $index= 'PRIMARY';
+ my $i= new MySQL::NDB::Size::Index(
+ name => $index,
+ unique => $indexes{$index}{unique},
+ comment => $indexes{$index}{comment},
+ type => $indexes{$index}{type},
+ columns => [@{$indexes{$index}{columns}}],
+ );
+ my $im41= 25; # keep old estimate for 4.1
+ $im41+= $t->columns->{$_}->dm foreach @{$indexes{$index}{columns}};
+ $i->im(16); # estimate from Johan
+ $i->dm(16) if $indexes{$index}{unique}; # estimate from Johan
+ $i->ver_im('4.1',$im41);
+
+ $t->indexes_set($index => $i);
+ $t->indexed_columns_set($_ => 1)
+ foreach @{$indexes{$index}{columns}};
}
- $TotalDataMemory[$_]{val}=$IndexDataMemory[$_]{val}+$totalsize[$_]+$RowOverhead[$_]{val}+$nrvarsize_mem[$_]{val} foreach 0..$#releases;
-
- my @RowDataMemory;
- push @RowDataMemory,{val=>$_} foreach @totalsize;
-
- my @RowPerPage;
- push @RowPerPage,{val=>(floor((32768-128)/$TotalDataMemory[$_]{val}))} foreach 0..$#TotalDataMemory;
-
- my @RowPerIndexPage;
- push @RowPerIndexPage,{val=>(floor(8192/$RowIndexMemory[$_]{val}))} foreach 0..$#TotalDataMemory;
-
- my @DataMemory;
- push @DataMemory,{val=>ceil(($count[0]/($RowPerPage[$_]{val})))*32} foreach 0..$#RowPerPage;
-
- my @IndexMemory;
- push @IndexMemory,{val=>ceil(($count[0]/($RowPerIndexPage[$_]{val})))*8} foreach 0..$#RowPerPage;
-
- my $count= $count[0];
- my @counts;
- $counts[$_]{val}= $count foreach 0..$#releases;
-
- my @nrvarsize_rel= ({val=>0},{val=>0},
- {val=>$nrvarsize}); #,{val=>0});
-
- push @table_size, {
- table=>$table,
- indexes=>\@indexes,
- columns=>\@columns,
- count=>\@counts,
- RowOverhead=>\@RowOverhead,
- RowDataMemory=>\@RowDataMemory,
- nrvarsize=>\@nrvarsize_rel,
- nrvarsize_mem=>\@nrvarsize_mem,
- releases=>\@releases,
- IndexDataMemory=>\@IndexDataMemory,
- TotalDataMemory=>\@TotalDataMemory,
- RowPerPage=>\@RowPerPage,
- DataMemory=>\@DataMemory,
- RowIndexMemory=>\@RowIndexMemory,
- RowPerIndexPage=>\@RowPerIndexPage,
- IndexMemory=>\@IndexMemory,
-
- };
-
- $dbDataMemory[$_]{val} += $DataMemory[$_]{val} foreach 0..$#releases;
- $dbIndexMemory[$_]{val} += $IndexMemory[$_]{val} foreach 0..$#releases;
- $NoOfAttributes[$_]{val} += @columns foreach 0..$#releases;
- $NoOfIndexes[$_]{val} += @indexes foreach 0..$#releases;
-}
+ foreach my $index (keys %indexes) {
+ next if $index eq 'PRIMARY';
+
+ if(!$indexes{$index}{unique})
+ {
+ my $i= new MySQL::NDB::Size::Index(
+ name => $index,
+ unique => $indexes{$index}{unique},
+ comment => $indexes{$index}{comment},
+ type => $indexes{$index}{type},
+ columns => [@{$indexes{$index}{columns}}],
+ );
+ $i->dm(16);
+ $t->indexes_set($index => $i);
+ $t->indexed_columns_set($_ => 1)
+ foreach @{$indexes{$index}{columns}};
+ }
+ else
+ {
+ my $i= new MySQL::NDB::Size::Index(
+ name => $index,
+ unique => $indexes{$index}{unique},
+ comment => $indexes{$index}{comment},
+ type => $indexes{$index}{type},
+ columns => [@{$indexes{$index}{columns}},
+ @{$t->indexes->{'PRIMARY'}->columns()}],
+ );
+
+ $i->is_supporting_table(1);
+ $t->indexes_set($index => $i);
+
+ my %idxcols;
+ foreach(@{$i->columns()})
+ {
+ $idxcols{$_} = $t->columns->{$_}
+ }
+ # Is a supporting table, add it to the lists:
+ my $idxname= $t->name().'_'.join('_',@{$indexes{$index}{columns}}).
+ "\$unique";
+ $report->supporting_tables_set($idxname => 1);
+ $t->supporting_tables_push($idxname);
+
+ $t->indexed_columns_set($_ => 1)
+ foreach @{$indexes{$index}{columns}};
+
+ my $st= new MySQL::NDB::Size::Table(name => $idxname,
+ rows => $count[0],
+ row_dm_overhead =>
+ { '4.1' => 12,
+ '5.0' => 12,
+ '5.1' => 16+4,
+ },
+ row_vdm_overhead =>
+ { '5.1' => 8 },
+ row_ddm_overhead =>
+ { '5.1' => 8 },
+ );
+
+ do_table($st,
+ \%idxcols,
+ {
+ 'PRIMARY' => {
+ 'unique' => 0,#$indexes{$index}{unique},
+ 'columns' => [@{$indexes{$index}{columns}}],
+ 'type' => 'BTREE',
+ }
+ },
+ \@count);
+ }
+ }
+
+ $t->compute_row_size($report->versions);
+
+} # do_table
foreach(@{$tables})
{
my $table= @{$_}[0];
- my $info= $dbh->selectall_hashref('describe `'.$table.'`',"Field");
- my @count = $dbh->selectrow_array('select count(*) from `'.$table.'`');
+ my $info;
+ {
+ my $sql= 'describe `'.$table.'`';
+ if($loadqueries)
+ {
+ $info= $queries{$sql};
+ }
+ else
+ {
+ $info= $dbh->selectall_hashref($sql,"Field");
+ $queries{$sql}= $info;
+ }
+ }
+ my @count;
+ {
+ my $sql= 'select count(*) from `'.$table.'`';
+ if($loadqueries)
+ {
+ @count= @{$queries{$sql}};
+ }
+ else
+ {
+ @count= $dbh->selectrow_array($sql);
+ $queries{$sql}= \@count;
+ }
+ }
my %indexes;
{
- my $sth= $dbh->prepare("show index from `".$table.'`');
- $sth->execute;
- while(my $i = $sth->fetchrow_hashref)
- {
+ my @show_indexes;
+ {
+ my $sql= "show index from `".$table.'`';
+ if($loadqueries)
+ {
+ @show_indexes= @{$queries{$sql}};
+ }
+ else
+ {
+ my $sth= $dbh->prepare($sql);
+ $sth->execute;
+ while(my $i = $sth->fetchrow_hashref)
+ {
+ push @show_indexes, $i;
+ }
+ $queries{$sql}= \@show_indexes;
+ }
+ }
+ foreach my $i(@show_indexes)
+ {
$indexes{${%$i}{Key_name}}= {
type=>${%$i}{Index_type},
unique=>!${%$i}{Non_unique},
comment=>${%$i}{Comment},
} if !defined($indexes{${%$i}{Key_name}});
- $indexes{${%$i}{Key_name}}{columns}[${%$i}{Seq_in_index}-1]=
+ $indexes{${%$i}{Key_name}}{columns}[${%$i}{Seq_in_index}-1]=
${%$i}{Column_name};
}
}
- do_table($table, $info, \%indexes, \@count);
-}
-
-my @NoOfTriggers;
-# for unique hash indexes
-$NoOfTriggers[$_]{val} += $NoOfIndexes[$_]{val}*3 foreach 0..$#releases;
-# for ordered index
-$NoOfTriggers[$_]{val} += $NoOfIndexes[$_]{val} foreach 0..$#releases;
-
-my @ParamMemory;
-foreach (0..$#releases) {
- $ParamMemory[0]{releases}[$_]{val}= POSIX::ceil(200*$NoOfAttributes[$_]{val}/1024);
- $ParamMemory[0]{name}= 'Attributes';
-
- $ParamMemory[1]{releases}[$_]{val}= 20*$NoOfTables[$_]{val};
- $ParamMemory[1]{name}= 'Tables';
-
- $ParamMemory[2]{releases}[$_]{val}= 10*$NoOfIndexes[$_]{val};
- $ParamMemory[2]{name}= 'OrderedIndexes';
-
- $ParamMemory[3]{releases}[$_]{val}= 15*$NoOfIndexes[$_]{val};
- $ParamMemory[3]{name}= 'UniqueHashIndexes';
-}
-
-$template->param(tables => \@table_size);
-$template->param(Parameters => [{name=>'DataMemory (kb)',
- releases=>\@dbDataMemory},
- {name=>'IndexMemory (kb)',
- releases=>\@dbIndexMemory},
- {name=>'MaxNoOfTables',
- releases=>\@NoOfTables},
- {name=>'MaxNoOfAttributes',
- releases=>\@NoOfAttributes},
- {name=>'MaxNoOfOrderedIndexes',
- releases=>\@NoOfIndexes},
- {name=>'MaxNoOfUniqueHashIndexes',
- releases=>\@NoOfIndexes},
- {name=>'MaxNoOfTriggers',
- releases=>\@NoOfTriggers}
- ]
- );
-$template->param(ParamMemory => \@ParamMemory);
-
-print $template->output;
+ my $t= new MySQL::NDB::Size::Table(name => $table,
+ rows => $count[0],
+ row_dm_overhead =>
+ { '4.1' => 12,
+ '5.0' => 12,
+ '5.1' => 16,
+ },
+ row_vdm_overhead => { '5.1' => 8 },
+ row_ddm_overhead => { '5.1' => 8 },
+ );
+
+
+ do_table($t, $info, \%indexes, \@count);
+}
+
+# compute table estimates
+while(my ($tname,$t)= $report->tables_each())
+{
+ $t->compute_estimate();
+}
+
+# Now parameters....
+
+$report->parameters_set('NoOfTables' =>
+ new MySQL::NDB::Size::Parameter(name=>'NoOfTables',
+ mem_per_item=>20,
+ default=>128)
+ );
+
+$report->parameters->{'NoOfTables'}->value_set($_ => scalar @{$report->tables_keys()})
+ foreach $report->versions;
+
+$report->parameters_set('NoOfAttributes' =>
+ new MySQL::NDB::Size::Parameter(name=>'NoOfAttributes',
+ mem_per_item=>0.2,
+ default=>1000)
+ );
+
+{
+ my $attr= 0;
+ while(my ($tname,$t)= $report->tables_each())
+ {
+ $attr+= scalar @{$t->columns_keys()};
+ }
+ $report->parameters->{'NoOfAttributes'}->value_set($_ => $attr)
+ foreach $report->versions;
+}
+
+
+$report->parameters_set('NoOfOrderedIndexes' =>
+ new MySQL::NDB::Size::Parameter(name=>'NoOfOrderedIndexes',
+ mem_per_item=>10,
+ default=>128)
+ );
+{
+ my $attr= 0;
+ while(my ($tname,$t)= $report->tables_each())
+ {
+ next if $report->supporting_tables_exists($tname);
+ $attr+= scalar @{$t->indexes_keys()};
+ }
+ $report->parameters->{'NoOfOrderedIndexes'}->value_set($_ => $attr)
+ foreach $report->versions;
+}
+
+$report->parameters_set('NoOfUniqueHashIndexes' =>
+ new MySQL::NDB::Size::Parameter(name=>'NoOfUniqueHashIndexes',
+ mem_per_item=>15,
+ default=>64)
+ );
+{
+ my $attr= 0;
+ while(my ($tname,$t)= $report->tables_each())
+ {
+ next if not $tname =~ /\$unique$/;
+ $attr++;
+ }
+ $report->parameters->{'NoOfUniqueHashIndexes'}->value_set($_ => $attr)
+ foreach $report->versions;
+}
+
+# Size of trigger is not documented
+$report->parameters_set('NoOfTriggers' =>
+ new MySQL::NDB::Size::Parameter(name=>'NoOfTriggers',
+ mem_per_item=>0,
+ default=>768)
+ );
+
+{
+ $report->parameters->{'NoOfTriggers'}->value_set(
+ $_ => (
+ (3*
+ $report->parameters->{'NoOfUniqueHashIndexes'}->value->{$_})
+ +
+ $report->parameters->{'NoOfOrderedIndexes'}->value->{$_}
+ +
+ (4* # for backups (3) and replication (1??)
+ $report->parameters->{'NoOfTables'}->value->{$_})
+
+ )
+ )
+ foreach $report->versions;
+}
+
+# DataMemory is in bytes...
+$report->parameters_set('DataMemory' =>
+ new MySQL::NDB::Size::Parameter(name=>'DataMemory',
+ mem_per_item=>1024,
+ unit=>'KB',
+ default=>80*1024)
+ );
+$report->parameters_set('IndexMemory' =>
+ new MySQL::NDB::Size::Parameter(name=>'IndexMemory',
+ mem_per_item=>1024,
+ unit=>'KB',
+ default=>18*1024)
+ );
+
+{
+ foreach my $ver ($report->versions)
+ {
+ my $dm=0;
+ my $im=0;
+ while(my ($tname,$t)= $report->tables_each())
+ {
+ $dm+=$t->dm_needed->{$ver};
+ $dm+=$t->vdm_needed->{$ver} || 0;
+ $im+=$t->im_needed->{$ver};
+ }
+ $report->parameters->{'DataMemory'}->value_set($ver => $dm/1024);
+ $report->parameters->{'IndexMemory'}->value_set($ver => $im/1024);
+ }
+}
+
+
+if($savequeries)
+{
+ open Q, "> $savequeries";
+ print Q Dumper(\%queries);
+ close Q;
+}
+
+use Data::Dumper;
+
+if($debug)
+{
+ eval 'print STDERR Dumper($report)';
+}
+
+if($format eq 'text')
+{
+ my $text_out= new MySQL::NDB::Size::Output::Text($report);
+ $text_out->output();
+}
+elsif($format eq 'html')
+{
+ my $html_out= new MySQL::NDB::Size::Output::HTML($report);
+ $html_out->output();
+}
+else
+{
+ # default to text output
+ my $text_out= new MySQL::NDB::Size::Output::Text($report);
+ $text_out->output();
+}
+
+package MySQL::NDB::Size::Output::Text;
+use Data::Dumper;
+
+sub new { bless { report=> $_[1] }, $_[0]}
+
+sub ul
+{
+ my $s= $_[1]."\n";
+ $s.='-' foreach (1..length($_[1]));
+ return $s.="\n";
+}
+
+sub output
+{
+ my $self= shift;
+ my $r= $self->{report};
+
+ print $self->ul("ndb_size.pl report for database ". $r->database().
+ " (".(($r->tables_count()||0)-($r->supporting_tables_count()||0)).
+ " tables)");
+
+ print "Connected to: ".$r->dsn()."\n\n";
+
+ print "Including information for versions: ".
+ join(', ',@{$r->versions})."\n\n";
+
+ foreach my $tname (@{$r->tables_keys()})
+ {
+ my $t= $r->tables->{$tname};
+# next if $r->supporting_tables_exists($tname);
+
+ print $self->ul($tname)."\n";
+
+ # format strings
+ my $f= "%25s ";
+ my $v= "%10s ";
+
+ # Columns
+ print "DataMemory for Columns (* means varsized DataMemory):\n";
+ printf $f.'%20s %9s %5s','Column Name','Type','Varsized', 'Key';
+ printf $v, $_ foreach @{$r->versions};
+ print "\n";
+ my %dm_totals;
+ my %vdm_totals;
+ while(my ($cname, $c)= $t->columns_each())
+ {
+ $c->type =~ /^([^\(]*)/g;
+ printf $f.'%20s %9s %5s',
+ $cname,
+ $1.(
+ ( $c->size and not $c->type() =~ /(enum|set)/)
+ ? '('.$c->size.')'
+ :'' ),
+ ($c->is_varsize)? 'Y':' ',
+ (defined($c->Key))?$c->Key:' ';
+ foreach(@{$r->versions})
+ {
+ if($c->ver_dm_exists($_))
+ {
+ printf $v, $c->ver_dm($_).(($c->is_varsize)?'*':'');
+ if($c->is_varsize())
+ {
+ $vdm_totals{$_}+= $c->ver_dm($_);
+ }
+ else
+ {
+ $dm_totals{$_}+= $c->ver_dm($_);
+ }
+ }
+ else
+ {
+ printf $v, $c->dm||'N/A';
+ $dm_totals{$_}+=$c->dm||0;
+ }
+ }
+ print "\n";
+ }
+ printf $f.'%20s %9s %5s','','','', '';
+ printf $v, '--' foreach @{$t->dm_versions};
+ print "\n";
+ printf $f.'%20s %9s %5s','Fixed Size Columns DM/Row','','','';
+ printf $v, $dm_totals{$_} foreach @{$r->versions};
+ print "\n";
+ printf $f.'%20s %9s %5s','Varsize Columns DM/Row','','','';
+ printf $v, $vdm_totals{$_} || 0 foreach @{$r->versions};
+ print "\n";
+
+
+ # DM for Indexes
+ print "\n\nDataMemory for Indexes:\n";
+ printf $f.'%20s ','Index Name','Type';
+ printf $v, $_ foreach @{$r->versions};
+ print "\n";
+ my %idx_dm_totals;
+ while(my ($iname, $i)= $t->indexes_each())
+ {
+ printf $f.'%20s ',$iname,$i->type();
+ foreach(@{$r->versions})
+ {
+ if($i->ver_dm_exists($_))
+ {
+ printf $v, $i->ver_dm($_).(($i->is_varsize)?'*':'');
+ $idx_dm_totals{$_}+= $i->ver_dm($_);
+ }
+ else
+ {
+ printf $v, ((defined($i->dm))?$i->dm:'N/A');
+ $idx_dm_totals{$_}+= $i->dm if defined($i->dm);
+ }
+ }
+ print "\n";
+ }
+ printf $f.'%20s ','','';
+ printf $v, '--' foreach @{$r->versions};
+ print "\n";
+ printf $f.'%20s ','Total Index DM/Row','';
+ printf $v, (defined($idx_dm_totals{$_}))?$idx_dm_totals{$_}:0
+ foreach @{$r->versions};
+ print "\n\n";
+
+ if(@{$t->supporting_tables()})
+ {
+ print "\n\nSupporting Tables DataMemory/Row";
+ my %supp_total;
+ foreach(@{$t->supporting_tables()})
+ {
+ print "\n";
+ printf $f, $_;
+ my $st= $r->tables->{$_};
+ printf $v, $st->row_dm_size->{$_} foreach @{$st->dm_versions};
+ $supp_total{$_}+=$st->row_dm_size->{$_}
+ foreach @{$st->dm_versions};
+ }
+ print "\n";
+ printf $f, '';
+ printf $v, '--' foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'This DataMemory/Row';
+ printf $v, $t->row_dm_size->{$_} foreach @{$t->dm_versions};
+ $supp_total{$_}+=$t->row_dm_size->{$_}
+ foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'Total DM/Row';
+ printf $v, $supp_total{$_} foreach @{$t->dm_versions};
+ print " Includes DM in other tables\n";
+ }
+
+ # IM for Columns
+ print "\n\nIndexMemory for Indexes:\n";
+ printf $f,'Index Name';
+ printf $v, $_ foreach @{$r->versions};
+ print "\n";
+ my %im_totals;
+ foreach my $iname (@{$t->indexes_keys()})
+ {
+ my $i= $t->indexes->{$iname};
+ next if $i->is_supporting_table();
+
+ printf $f, $iname;
+
+ foreach(@{$r->versions})
+ {
+ if(!defined($i->im))
+ {
+ printf $v,'N/A';
+ next;
+ }
+ if($i->ver_im_exists($_))
+ {
+ printf $v, $i->ver_im->{$_};
+ $im_totals{$_}+= $i->ver_im->{$_};
+ }
+ else
+ {
+ printf $v, $i->im;
+ $im_totals{$_}+=$i->im;
+ }
+ }
+ print "\n";
+ }
+ printf $f,'';
+ printf $v, '--' foreach @{$t->dm_versions};
+ print "\n";
+ printf $f,'Indexes IM/Row';
+ printf $v, $im_totals{$_} foreach @{$r->versions};
+ print "\n";
+
+ if(@{$t->supporting_tables()})
+ {
+ print "\n\nSupporting Tables IndexMemory/Row";
+ my %supp_total;
+ foreach(@{$t->supporting_tables()})
+ {
+ print "\n";
+ my $st= $r->tables->{$_};
+ foreach(@{$st->indexes_keys()})
+ {
+ printf $f, $st->name() if $_ eq 'PRIMARY';
+ printf $f, $st->name().$_ if $_ ne 'PRIMARY';
+ my $sti= $st->indexes->{$_};
+ printf $v, ($sti->ver_im_exists($_))
+ ?$sti->ver_im->{$_}
+ :$sti->im() foreach @{$st->dm_versions};
+ $supp_total{$_}+= ($sti->ver_im_exists($_))
+ ?$sti->ver_im->{$_}
+ :$sti->im() foreach @{$st->dm_versions};
+
+ }
+ }
+ print "\n";
+ printf $f, '';
+ printf $v, '--' foreach @{$t->dm_versions};
+ print "\n";
+ print "\n";
+ printf $f, 'Total Suppt IM/Row';
+ printf $v, $supp_total{$_} foreach @{$t->dm_versions};
+ print "\n";
+ }
+
+ print "\n\n\nSummary (for THIS table):\n";
+ printf $f, '';
+ printf $v, $_ foreach @{$r->versions};
+ print "\n";
+ printf $f, 'Fixed Overhead DM/Row';
+ printf $v, $t->row_dm_overhead->{$_} foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'NULL Bytes/Row';
+ printf $v, $t->dm_null_bytes->{$_}||0 foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'DataMemory/Row';
+ printf $v, $t->row_dm_size->{$_} foreach @{$t->dm_versions};
+ print " (Includes overhead, bitmap and indexes)\n";
+
+ print "\n";
+ printf $f, 'Varsize Overhead DM/Row';
+ printf $v, $t->row_vdm_overhead->{$_}||0 foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'Varsize NULL Bytes/Row';
+ printf $v, $t->vdm_null_bytes->{$_}||0 foreach @{$t->dm_versions};
+ print "\n";
+ printf $f, 'Avg Varside DM/Row';
+ printf $v, (exists($t->row_vdm_size->{$_})?
+ $t->row_vdm_size->{$_}: 0)
+ foreach @{$r->versions};
+ print "\n\n";
+ printf $f, 'No. Rows';
+ printf $v, $t->rows foreach @{$r->versions};
+ print "\n\n";
+ printf $f, 'Rows/'.($t->dm_pagesize()/1024).'kb DM Page';
+ printf $v, $t->dm_rows_per_page->{$_} foreach @{$r->versions};
+ print "\n";
+ printf $f, 'Fixedsize DataMemory (KB)';
+ printf $v, $t->dm_needed->{$_}/1024 foreach @{$r->versions};
+ print "\n\n";
+ printf $f, 'Rows/'.($t->vdm_pagesize()/1024).'kb Varsize DM Page';
+ printf $v, $t->vdm_rows_per_page->{$_}||0 foreach @{$r->versions};
+ print "\n";
+ printf $f, 'Varsize DataMemory (KB)';
+ printf $v, ($t->vdm_needed->{$_}||0)/1024 foreach @{$r->versions};
+ print "\n\n";
+ printf $f, 'Rows/'.($t->im_pagesize()/1024).'kb IM Page';
+ printf $v, $t->im_rows_per_page->{$_} foreach @{$r->versions};
+ print "\n";
+ printf $f, 'IndexMemory (KB)';
+ printf $v, $t->im_needed->{$_}/1024 foreach @{$r->versions};
+
+ print "\n\n\n";
+ }
+
+ print "\n\n\n";
+ print $self->ul("Parameter Minimum Requirements");
+ print "* indicates greater than default\n\n";
+ printf "%25s ","Parameter";
+ printf "%15s ",'Default' ;
+ printf "%15s%1s ",$_,'' foreach @{$r->versions};
+ print "\n";
+ while( my ($pname, $p)= $r->parameters_each())
+ {
+ printf "%25s ",$pname.(($p->unit)?' ('.$p->unit.')':'');
+ printf "%15u ", $p->default;
+ printf "%15u%1s ", $p->value->{$_},
+ ($p->value->{$_} > $p->default)?'*':''
+ foreach @{$r->versions};
+ print "\n";
+ }
+ print "\n\n\n";
+}
+
+sub table
+{
+ my $self= shift;
+ my $t= shift;
+}
+
+package MySQL::NDB::Size::Output::HTML;
+
+sub new { bless { report=> $_[1] }, $_[0]}
+
+sub tag
+{
+ my ($self,$tag,$content)= @_;
+ return "<$tag>$content</$tag>\n";
+}
+
+sub h1 { my ($self,$t)= @_; return $self->tag('h1',$t); }
+sub h2 { my ($self,$t)= @_; return $self->tag('h2',$t); }
+sub h3 { my ($self,$t)= @_; return $self->tag('h3',$t); }
+sub h4 { my ($self,$t)= @_; return $self->tag('h4',$t); }
+
+sub p { my ($self,$t)= @_; return $self->tag('p',$t); }
+sub b { my ($self,$t)= @_; return $self->tag('b',$t); }
+
+sub th
+{
+ my ($self)= shift;
+ my $c;
+ $c.=$self->tag('th',$_) foreach @_;
+ return $self->tag('tr',$c);
+}
+
+sub tr
+{
+ my ($self)= shift;
+ my $c;
+ $c.=$self->tag('td',$_) foreach @_;
+ return $self->tag('tr',$c);
+}
+
+sub td { my ($self,$t)= @_; return $self->tag('td',$t); }
+
+sub ul
+{
+ my ($self)= shift;
+ my $c;
+ $c.= " ".$self->li($_) foreach @_;
+ return $self->tag('ul',$c);
+}
+
+sub li { my ($self,$t)= @_; return $self->tag('li',$t); }
+
+sub href
+{
+ my ($self,$href,$t)= @_;
+ $href =~ s/\$/__/g;
+ return "<a href=\"$href\">$t</a>";
+}
+
+sub aname
+{
+ my ($self,$href,$t)= @_;
+ $href =~ s/\$/__/g;
+ return "<a id=\"$href\">$t</a>";
+}
+
+sub output
+{
+ my $self= shift;
+ my $r= $self->{report};
+
+ print <<ENDHTML;
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+ <head>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
+ <meta name="keywords" content="MySQL Cluster" />
+ENDHTML
+print "<title>MySQL Cluster size estimate for ".$r->database()."</title>";
+print <<ENDHTML;
+ <style type="text/css">
+ table { border-collapse: collapse }
+ td,th { border: 1px solid black }
+ </style>
+ </head>
+<body>
+ENDHTML
+
+ print $self->h1("ndb_size.pl report for database ". $r->database().
+ " (".(($r->tables_count()||0)-($r->supporting_tables_count()||0)).
+ " tables)");
+
+ print $self->p("Connected to: ".$r->dsn());
+
+ print $self->p("Including information for versions: ".
+ join(', ',@{$r->versions}));
+
+ if(@{$r->tables_keys()})
+ {
+ print $self->h2("Table List");
+ my @tlist;
+ foreach(sort @{$r->tables_keys()})
+ {
+ push @tlist, $self->href("#$_",$_);
+ }
+ print $self->ul(@tlist);
+ }
+
+ foreach my $tname (sort @{$r->tables_keys()})
+ {
+ my $t= $r->tables->{$tname};
+
+ print $self->h2($self->aname($tname,$tname));
+
+ # format strings
+ my $f= "%25s ";
+ my $v= "%10s ";
+
+ # Columns
+ print $self->h3("DataMemory for Columns");
+ print $self->p("* means varsized DataMemory");
+ print "<table>\n";
+ print $self->th('Column Name','Type','Varsized', 'Key',
+ @{$r->versions});
+
+ my %dm_totals;
+ my %vdm_totals;
+ while(my ($cname, $c)= $t->columns_each())
+ {
+ $c->type =~ /^([^\(]*)/g;
+ my @verinfo;
+ foreach(@{$r->versions})
+ {
+ if($c->ver_dm_exists($_))
+ {
+ push @verinfo, $c->ver_dm($_).(($c->is_varsize)?'*':'');
+ if($c->is_varsize())
+ {
+ $vdm_totals{$_}+= $c->ver_dm($_);
+ }
+ else
+ {
+ $dm_totals{$_}+= $c->ver_dm($_);
+ }
+ }
+ else
+ {
+ push @verinfo, $c->dm||'N/A';
+ $dm_totals{$_}+=$c->dm||0;
+ }
+ }
+
+ print $self->tr(
+ $cname,
+ $1.(
+ ( $c->size and not $c->type() =~ /(enum|set)/)
+ ? '('.$c->size.')'
+ :'' ),
+ ($c->is_varsize)? 'Y':' ',
+ (defined($c->Key))?$c->Key:' ',@verinfo);
+ }
+
+ {
+ my @dmtot;
+ push @dmtot, $self->b($dm_totals{$_}) foreach @{$r->versions};
+ print $self->tr($self->b('Fixed Size Columns DM/Row'),'','','',
+ @dmtot);
+
+ }
+ {
+ my @vdmtot;
+ push @vdmtot, $self->b($vdm_totals{$_} || 0)
+ foreach @{$r->versions};
+ print $self->tr($self->b('Varsize Columns DM/Row'),'','','',
+ @vdmtot);
+ }
+
+ print "</table>\n";
+
+ # DM for Indexes
+ print $self->h3('DataMemory for Indexes');
+ print "<table>\n";
+ print $self->th('Index Name','Type',@{$r->versions});
+
+ my %idx_dm_totals;
+ while(my ($iname, $i)= $t->indexes_each())
+ {
+ my @verinfo;
+ foreach(@{$r->versions})
+ {
+ if($i->ver_dm_exists($_))
+ {
+ push @verinfo, $i->ver_dm($_).(($i->is_varsize)?'*':'');
+ $idx_dm_totals{$_}+= $i->ver_dm($_);
+ }
+ else
+ {
+ push @verinfo, ((defined($i->dm))?$i->dm:'N/A');
+ $idx_dm_totals{$_}+= $i->dm if defined($i->dm);
+ }
+ }
+ printf $self->tr($iname,$i->type(),@verinfo);
+ }
+ {
+ my @idxtot;
+ push @idxtot, $self->b((defined($idx_dm_totals{$_}))
+ ? $idx_dm_totals{$_}:0)
+ foreach @{$r->versions};
+ print $self->tr($self->b('Total Index DM/Row'),'',
+ @idxtot);
+ }
+
+ print "</table>";
+
+ if(@{$t->supporting_tables()})
+ {
+ print $self->h3("Supporting Tables DataMemory/Row");
+ my %supp_total;
+
+ print "<table>";
+ print $self->th('Table',@{$r->versions});
+ foreach(@{$t->supporting_tables()})
+ {
+ my $st= $r->tables->{$_};
+ my @stdm;
+ push @stdm, $st->row_dm_size->{$_} foreach @{$st->dm_versions};
+
+ print $self->tr($_,@stdm);
+
+ $supp_total{$_}+=$st->row_dm_size->{$_}
+ foreach @{$st->dm_versions};
+ }
+ {
+ my @rdmtot;
+ push @rdmtot, $self->b($t->row_dm_size->{$_})
+ foreach @{$t->dm_versions};
+ print $self->tr($self->b('This DataMemory/Row'),@rdmtot);
+ }
+ $supp_total{$_}+=$t->row_dm_size->{$_}
+ foreach @{$t->dm_versions};
+
+ {
+ my @tdmr;
+ push @tdmr, $self->b($supp_total{$_})
+ foreach @{$t->dm_versions};
+ print $self->tr($self->b('Total DM/Row (inludes DM in other tables)'),@tdmr);
+ }
+ print "</table>";
+ }
+
+ # IM for Columns
+ print $self->h3("IndexMemory for Indexes");
+ print "<table>\n";
+ print $self->th('Index Name', @{$r->versions});
+
+ my %im_totals;
+ foreach my $iname (@{$t->indexes_keys()})
+ {
+ my $i= $t->indexes->{$iname};
+ next if $i->is_supporting_table();
+
+ my @verinfo;
+ foreach(@{$r->versions})
+ {
+ if(!defined($i->im))
+ {
+ push @verinfo,'N/A';
+ next;
+ }
+ if($i->ver_im_exists($_))
+ {
+ push @verinfo, $i->ver_im->{$_};
+ $im_totals{$_}+= $i->ver_im->{$_};
+ }
+ else
+ {
+ push @verinfo, $i->im;
+ $im_totals{$_}+=$i->im;
+ }
+ }
+ print $self->tr($iname, @verinfo);
+ }
+ {
+ my @v;
+ push @v, $self->b($im_totals{$_}) foreach @{$r->versions};
+ printf $self->tr('Indexes IM/Row',@v);
+ }
+ print "</table>\n";
+
+ if(@{$t->supporting_tables()})
+ {
+ print $self->h3("Supporting Tables IndexMemory/Row");
+ print "<table>\n";
+ my %supp_total;
+ foreach(@{$t->supporting_tables()})
+ {
+ my $st= $r->tables->{$_};
+ foreach(@{$st->indexes_keys()})
+ {
+ my @r;
+ push @r, $st->name() if $_ eq 'PRIMARY';
+ push @r, $st->name().$_ if $_ ne 'PRIMARY';
+ my $sti= $st->indexes->{$_};
+ push @r, ($sti->ver_im_exists($_))
+ ?$sti->ver_im->{$_}
+ :$sti->im() foreach @{$st->dm_versions};
+ $supp_total{$_}+= ($sti->ver_im_exists($_))
+ ?$sti->ver_im->{$_}
+ :$sti->im() foreach @{$st->dm_versions};
+ print $self->tr(@r);
+ }
+ }
+ {
+ my @r;
+ push @r, $self->b($supp_total{$_}) foreach @{$t->dm_versions};
+ print $self->tr($self->b('Total Suppt IM/Row'),@r);
+ }
+ print "</table>\n";
+ }
+
+ print $self->h3("Summary (for THIS table)");
+ print $self->h4("Fixed Sized Part");
+ print "<table>\n";
+
+ print $self->tr('',@{$r->versions});
+
+ { my @r;
+ push @r, $t->row_dm_overhead->{$_} foreach @{$t->dm_versions};
+ print $self->tr('Fixed Overhead DM/Row',@r);
+ }
+ { my @r;
+ push @r, $t->dm_null_bytes->{$_}||0 foreach @{$t->dm_versions};
+ print $self->tr('NULL Bytes/Row',@r);
+ }
+ { my @r;
+ push @r, $t->row_dm_size->{$_} foreach @{$t->dm_versions};
+ print $self->tr('DataMemory/Row (incl overhead, bitmap, indexes)',
+ @r);
+ }
+ print "</table>\n";
+ print $self->h4("Variable Sized Part");
+ print "<table>\n";
+
+ { my @r;
+ push @r, $t->row_vdm_overhead->{$_}||0 foreach @{$t->dm_versions};
+ print $self->tr('Varsize Overhead DM/Row',@r);
+ }
+ { my @r;
+ push @r, $t->vdm_null_bytes->{$_}||0 foreach @{$t->dm_versions};
+ print $self->tr('Varsize NULL Bytes/Row',@r);
+ }
+ { my @r;
+ push @r, (exists($t->row_vdm_size->{$_})?
+ $t->row_vdm_size->{$_}: 0)
+ foreach @{$r->versions};
+ print $self->tr('Avg Varside DM/Row',@r);
+ }
+ print "</table>\n";
+ print $self->h4("Memory Calculations");
+ print "<table>\n";
+
+ { my @r;
+ push @r, $t->rows foreach @{$r->versions};
+ print $self->tr('No. Rows',@r);
+ }
+ { my @r;
+ push @r, $t->dm_rows_per_page->{$_} foreach @{$r->versions};
+ print $self->tr('Rows/'.($t->dm_pagesize()/1024).'kb DM Page',@r);
+ }
+ { my @r;
+ push @r, $t->dm_needed->{$_}/1024 foreach @{$r->versions};
+ print $self->tr('Fixedsize DataMemory (KB)',@r);
+ }
+ { my @r;
+ push @r, $t->vdm_rows_per_page->{$_}||0 foreach @{$r->versions};
+ print $self->tr('Rows/'.($t->vdm_pagesize()/1024).
+ 'kb Varsize DM Page', @r);
+ }
+ { my @r;
+ push @r, ($t->vdm_needed->{$_}||0)/1024 foreach @{$r->versions};
+ print $self->tr('Varsize DataMemory (KB)', @r);
+ }
+ { my @r;
+ push @r, $t->im_rows_per_page->{$_} foreach @{$r->versions};
+ print $self->tr('Rows/'.($t->im_pagesize()/1024).'kb IM Page', @r);
+ }
+ { my @r;
+ push @r, $t->im_needed->{$_}/1024 foreach @{$r->versions};
+ print $self->tr('IndexMemory (KB)', @r);
+ }
+
+ print "</table><hr/>\n\n";
+ }
+
+ print $self->h1("Parameter Minimum Requirements");
+ print $self->p("* indicates greater than default");
+ print "<table>\n";
+ print $self->th("Parameter",'Default',@{$r->versions});
+ while( my ($pname, $p)= $r->parameters_each())
+ {
+ my @r;
+ push @r, $p->value->{$_}.
+ (($p->value->{$_} > $p->default)?'*':'')
+ foreach @{$r->versions};
+
+ print $self->tr($pname.(($p->unit)?' ('.$p->unit.')':''),
+ $p->default,
+ @r);
+ }
+ print "</table></body></html>";
+}
+
+sub table
+{
+ my $self= shift;
+ my $t= shift;
+}
diff --git a/storage/ndb/tools/ndb_size.tmpl b/storage/ndb/tools/ndb_size.tmpl
deleted file mode 100644
index 1e19ea132ba..00000000000
--- a/storage/ndb/tools/ndb_size.tmpl
+++ /dev/null
@@ -1,231 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/>
- <meta name="keywords" content="MySQL Cluster" />
- <title>MySQL Cluster size estimate for <TMPL_VAR NAME="db" ESCAPE="HTML"></title>
-<style type="text/css">
-table { border-collapse: collapse }
-td,th { border: 1px solid black }
-</style>
- </head>
-<body>
-<h1>MySQL Cluster analysis for <TMPL_VAR NAME="db" escape="html"></h1>
-<p>This is an automated analysis of the <TMPL_VAR NAME="DSN" escape="html"> database for migration into <a href="http://www.mysql.com/">MySQL</a> Cluster. No warranty is made to the accuracy of the information.</p>
-
-<p>This information should be valid for MySQL 4.1 and 5.0. Since 5.1 is not a final release yet, the numbers should be used as a guide only.</p>
-
-<p>5.1-dd is for tables stored on disk. The ndb_size.pl estimates are <b>experimental</b> and should not be trusted. Notably we don't take into account indexed columns being in DataMemory versus non-indexed on disk.</p>
-
-<h2>Parameter Settings</h2>
-<p><b>NOTE</b> the configuration parameters below do not take into account system tables and other requirements.</p>
-<table>
- <tr>
- <th>Parameter</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel></th>
- </TMPL_LOOP>
- </tr>
-<TMPL_LOOP NAME=Parameters>
- <tr>
- <td><TMPL_VAR NAME=name></td>
- <TMPL_LOOP NAME=releases>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
- </tr>
-</TMPL_LOOP>
-</table>
-
-<h2>Memory usage because of parameters</h2>
-
-<p>Usage is in kilobytes. Actual usage will vary as you should set the parameters larger than those listed in the table above.</p>
-<table>
- <tr>
- <th>Parameter</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel></th>
- </TMPL_LOOP>
- </tr>
-<TMPL_LOOP NAME=ParamMemory>
- <tr>
- <td><TMPL_VAR NAME=name></td>
- <TMPL_LOOP NAME=releases>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
- </tr>
-</TMPL_LOOP>
-</table>
-
-<h2>Table List</h2>
-<ul>
-<TMPL_LOOP NAME="tables">
-<li><a href="#<TMPL_VAR NAME="table">"><TMPL_VAR NAME="table"></a></li>
-</TMPL_LOOP>
-</ul>
-
-<hr/>
-
-<TMPL_LOOP NAME="tables">
-<h2><a name="<TMPL_VAR NAME="table">"><TMPL_VAR NAME="table"></a></h2>
-<table>
- <tr>
- <th>Column</th>
- <th>Type</th>
- <th>VARSIZE</th>
- <th>Size</th>
- <th>Key</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel> NDB Size</th>
- </TMPL_LOOP>
- </tr>
- <TMPL_LOOP NAME="columns">
- <tr>
- <td><TMPL_VAR NAME=name></td>
- <td><TMPL_VAR NAME=type></td>
- <td><TMPL_IF NAME=is_varsize>YES<TMPL_ELSE>&nbsp;</TMPL_IF></td>
- <td><TMPL_VAR NAME=size></td>
- <td><TMPL_VAR NAME=key></td>
- <TMPL_LOOP NAME=datamemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
- </tr>
- </TMPL_LOOP>
-</table>
-
-<p>&nbsp;</p>
-
-<h3>Indexes</h3>
-
-<p>We assume that indexes are ORDERED (not created USING HASH). If order is not required, 10 bytes of data memory can be saved per row if the index is created USING HASH</p>
-<table>
-<tr>
- <th>Index</th>
- <th>Type</th>
- <th>Columns</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel> IdxMem</th>
- </TMPL_LOOP>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel> DatMem</th>
- </TMPL_LOOP>
-</tr>
-<TMPL_LOOP NAME="indexes">
- <tr>
- <td><TMPL_VAR NAME=name></td>
- <td><TMPL_VAR NAME=type></td>
- <td><TMPL_VAR NAME=columns></td>
- <TMPL_LOOP NAME=indexmemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
- <TMPL_LOOP NAME=datamemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
- </tr>
-</TMPL_LOOP>
-</table>
-
-<h3>DataMemory Usage</h3>
-<table>
-<tr>
- <th>&nbsp;</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel></th>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Nr Varsized Attributes</th>
- <TMPL_LOOP NAME=nrvarsize>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Row Overhead</th>
- <TMPL_LOOP NAME=RowOverhead>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Varsized Overhead</th>
- <TMPL_LOOP NAME=nrvarsize_mem>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Column DataMemory/Row</th>
- <TMPL_LOOP NAME=RowDataMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Index DataMemory/Row</th>
- <TMPL_LOOP NAME=IndexDataMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Total DataMemory/Row</th>
- <TMPL_LOOP NAME=TotalDataMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Rows per 32kb page</th>
- <TMPL_LOOP NAME=RowPerPage>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Current number of rows</th>
- <TMPL_LOOP NAME=count>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Total DataMemory (kb)</th>
- <TMPL_LOOP NAME=DataMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-</table>
-
-<h3>IndexMemory Usage</h3>
-<table>
-<tr>
- <th>&nbsp;</th>
- <TMPL_LOOP NAME=releases>
- <th><TMPL_VAR NAME=rel></th>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>IndexMemory/Row</th>
- <TMPL_LOOP NAME=RowIndexMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Rows per 8kb page</th>
- <TMPL_LOOP NAME=RowPerIndexPage>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Current number of rows</th>
- <TMPL_LOOP NAME=count>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-<tr>
- <th>Total IndexMemory (kb)</th>
- <TMPL_LOOP NAME=IndexMemory>
- <td><TMPL_VAR NAME=val></td>
- </TMPL_LOOP>
-</tr>
-</table>
-
-<hr/>
-</TMPL_LOOP>
-
-<p>This is the output of ndb_size.pl.</p>
-</body>
-</html>
-
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 0a9fbd4abe6..0b3007dba06 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -9055,7 +9055,9 @@ static void test_ts()
int rc, field_count;
char name;
char query[MAX_TEST_QUERY_LENGTH];
-
+ const char *queries [3]= {"SELECT a, b, c FROM test_ts WHERE %c=?",
+ "SELECT a, b, c FROM test_ts WHERE %c=?",
+ "SELECT a, b, c FROM test_ts WHERE %c=CAST(? AS DATE)"};
myheader("test_ts");
rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_ts");
@@ -9127,7 +9129,7 @@ static void test_ts()
{
int row_count= 0;
- sprintf(query, "SELECT a, b, c FROM test_ts WHERE %c=?", name);
+ sprintf(query, queries[field_count], name);
if (!opt_silent)
fprintf(stdout, "\n %s", query);