From: Ole John Aske Date: October 18 2011 10:44am Subject: bzr push into mysql-trunk-cluster branch (ole.john.aske:3392 to 3393) List-Archive: http://lists.mysql.com/commits/141491 Message-Id: <20111018104412.C2546232@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 3393 Ole John Aske 2011-10-18 Resurected code which implemented the new MRR interface from branch mysql-6.0-telco-7.0. Several fixes was required in order to make that code work as a looong time has passed since it was originally developed: - Addapted fix for Bug#57481:'multi range read' may fail to close completed NdbScanOperations' - Start transactions on demand when MRR operations are created. - Corrected bug in calculating size of MRR buffer to allocate. - Addapted code to correctly count pruned/sorted & plain scans in MRR - Addapted code to handle user specified partition. SPJ has not yet been integrated into the new MRR code - instead we will (temporary) use the default MRR impl when a join is pushed. Old MRR code is still present as a reference. (Plan to remove that in a later push) modified: mysql-test/suite/ndb/r/ndb_join_pushdown.result mysql-test/suite/ndb/r/ndb_read_multi_range.result mysql-test/suite/ndb/t/ndb_read_multi_range.test sql/ha_ndbcluster.cc sql/ha_ndbcluster.h sql/handler.cc storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp storage/ndb/src/ndbapi/NdbScanOperation.cpp 3392 Ole John Aske 2011-10-17 [merge] merge of mysql-5.5-cluster -> mysql-trunk-cluster removed: mysql-test/suite/ndb/t/memcache_basic.test storage/ndb/memcache/cache-src/ storage/ndb/memcache/cache-src/LICENSE storage/ndb/memcache/cache-src/assoc.c storage/ndb/memcache/cache-src/assoc.h storage/ndb/memcache/cache-src/default_engine.c storage/ndb/memcache/cache-src/default_engine.h storage/ndb/memcache/cache-src/items.c storage/ndb/memcache/cache-src/items.h storage/ndb/memcache/cache-src/slabs.c storage/ndb/memcache/cache-src/slabs.h storage/ndb/memcache/cache-src/trace.h added: mysql-test/suite/ndb/include/have_ndb_rqg.inc mysql-test/suite/ndb_big/bug37983-master.opt mysql-test/suite/ndb_big/disabled.def mysql-test/suite/ndb_big/rqg_spj-master.opt mysql-test/suite/ndb_big/rqg_spj.test mysql-test/suite/ndb_memcache/include/wait_for_reconf.inc mysql-test/suite/ndb_memcache/r/type_numeric.result mysql-test/suite/ndb_memcache/t/type_numeric.test storage/ndb/include/portlib/NdbGetRUsage.h storage/ndb/memcache/extra/ storage/ndb/memcache/extra/libevent/ storage/ndb/memcache/extra/libevent/CMakeLists.txt storage/ndb/memcache/extra/libevent/Doxyfile storage/ndb/memcache/extra/libevent/README storage/ndb/memcache/extra/libevent/WIN32-Code/ storage/ndb/memcache/extra/libevent/WIN32-Code/event-config.h storage/ndb/memcache/extra/libevent/WIN32-Code/misc.c storage/ndb/memcache/extra/libevent/WIN32-Code/misc.h storage/ndb/memcache/extra/libevent/WIN32-Code/tree.h storage/ndb/memcache/extra/libevent/WIN32-Code/win32.c storage/ndb/memcache/extra/libevent/WIN32-Prj/ storage/ndb/memcache/extra/libevent/WIN32-Prj/libevent.dsp storage/ndb/memcache/extra/libevent/WIN32-Prj/libevent.dsw storage/ndb/memcache/extra/libevent/WIN32-Prj/libevent.sln storage/ndb/memcache/extra/libevent/buffer.c storage/ndb/memcache/extra/libevent/compat/ storage/ndb/memcache/extra/libevent/compat/sys/ storage/ndb/memcache/extra/libevent/compat/sys/_time.h storage/ndb/memcache/extra/libevent/compat/sys/queue.h storage/ndb/memcache/extra/libevent/devpoll.c storage/ndb/memcache/extra/libevent/epoll.c storage/ndb/memcache/extra/libevent/epoll_sub.c storage/ndb/memcache/extra/libevent/evbuffer.c storage/ndb/memcache/extra/libevent/evdns.3 storage/ndb/memcache/extra/libevent/evdns.c storage/ndb/memcache/extra/libevent/evdns.h storage/ndb/memcache/extra/libevent/event-internal.h storage/ndb/memcache/extra/libevent/event.3 storage/ndb/memcache/extra/libevent/event.c storage/ndb/memcache/extra/libevent/event.h storage/ndb/memcache/extra/libevent/event_rpcgen.py storage/ndb/memcache/extra/libevent/event_tagging.c storage/ndb/memcache/extra/libevent/evhttp.h storage/ndb/memcache/extra/libevent/evport.c storage/ndb/memcache/extra/libevent/evrpc-internal.h storage/ndb/memcache/extra/libevent/evrpc.c storage/ndb/memcache/extra/libevent/evrpc.h storage/ndb/memcache/extra/libevent/evsignal.h storage/ndb/memcache/extra/libevent/evutil.c storage/ndb/memcache/extra/libevent/evutil.h storage/ndb/memcache/extra/libevent/http-internal.h storage/ndb/memcache/extra/libevent/http.c storage/ndb/memcache/extra/libevent/kqueue.c storage/ndb/memcache/extra/libevent/log.c storage/ndb/memcache/extra/libevent/log.h storage/ndb/memcache/extra/libevent/min_heap.h storage/ndb/memcache/extra/libevent/poll.c storage/ndb/memcache/extra/libevent/sample/ storage/ndb/memcache/extra/libevent/sample/Makefile.am storage/ndb/memcache/extra/libevent/sample/event-test.c storage/ndb/memcache/extra/libevent/sample/signal-test.c storage/ndb/memcache/extra/libevent/sample/time-test.c storage/ndb/memcache/extra/libevent/select.c storage/ndb/memcache/extra/libevent/signal.c storage/ndb/memcache/extra/libevent/strlcpy-internal.h storage/ndb/memcache/extra/libevent/strlcpy.c storage/ndb/memcache/extra/libevent/test/ storage/ndb/memcache/extra/libevent/test/Makefile.am storage/ndb/memcache/extra/libevent/test/bench.c storage/ndb/memcache/extra/libevent/test/regress.c storage/ndb/memcache/extra/libevent/test/regress.gen.c storage/ndb/memcache/extra/libevent/test/regress.gen.h storage/ndb/memcache/extra/libevent/test/regress.h storage/ndb/memcache/extra/libevent/test/regress.rpc storage/ndb/memcache/extra/libevent/test/regress_dns.c storage/ndb/memcache/extra/libevent/test/regress_http.c storage/ndb/memcache/extra/libevent/test/regress_rpc.c storage/ndb/memcache/extra/libevent/test/test-eof.c storage/ndb/memcache/extra/libevent/test/test-init.c storage/ndb/memcache/extra/libevent/test/test-time.c storage/ndb/memcache/extra/libevent/test/test-weof.c storage/ndb/memcache/extra/libevent/test/test.sh storage/ndb/memcache/extra/memcached/ storage/ndb/memcache/extra/memcached/AUTHORS storage/ndb/memcache/extra/memcached/CMakeLists.txt storage/ndb/memcache/extra/memcached/LICENSE storage/ndb/memcache/extra/memcached/config_static.h storage/ndb/memcache/extra/memcached/config_tests.in storage/ndb/memcache/extra/memcached/daemon/ storage/ndb/memcache/extra/memcached/daemon/cache.c storage/ndb/memcache/extra/memcached/daemon/cache.h storage/ndb/memcache/extra/memcached/daemon/daemon.c storage/ndb/memcache/extra/memcached/daemon/hash.c storage/ndb/memcache/extra/memcached/daemon/hash.h storage/ndb/memcache/extra/memcached/daemon/isasl.c storage/ndb/memcache/extra/memcached/daemon/isasl.h storage/ndb/memcache/extra/memcached/daemon/memcached.c storage/ndb/memcache/extra/memcached/daemon/memcached.h storage/ndb/memcache/extra/memcached/daemon/sasl_defs.c storage/ndb/memcache/extra/memcached/daemon/sasl_defs.h storage/ndb/memcache/extra/memcached/daemon/solaris_priv.c storage/ndb/memcache/extra/memcached/daemon/stats.c storage/ndb/memcache/extra/memcached/daemon/stats.h storage/ndb/memcache/extra/memcached/daemon/thread.c storage/ndb/memcache/extra/memcached/daemon/topkeys.c storage/ndb/memcache/extra/memcached/daemon/topkeys.h storage/ndb/memcache/extra/memcached/engines/ storage/ndb/memcache/extra/memcached/engines/default_engine/ storage/ndb/memcache/extra/memcached/engines/default_engine/assoc.c storage/ndb/memcache/extra/memcached/engines/default_engine/assoc.h storage/ndb/memcache/extra/memcached/engines/default_engine/default_engine.c storage/ndb/memcache/extra/memcached/engines/default_engine/default_engine.h storage/ndb/memcache/extra/memcached/engines/default_engine/items.c storage/ndb/memcache/extra/memcached/engines/default_engine/items.h storage/ndb/memcache/extra/memcached/engines/default_engine/slabs.c storage/ndb/memcache/extra/memcached/engines/default_engine/slabs.h storage/ndb/memcache/extra/memcached/extensions/ storage/ndb/memcache/extra/memcached/extensions/daemon/ storage/ndb/memcache/extra/memcached/extensions/daemon/stdin_check.c storage/ndb/memcache/extra/memcached/extensions/loggers/ storage/ndb/memcache/extra/memcached/extensions/loggers/blackhole_logger.c storage/ndb/memcache/extra/memcached/extensions/loggers/eventlog_logger.c storage/ndb/memcache/extra/memcached/extensions/loggers/syslog_logger.c storage/ndb/memcache/extra/memcached/extensions/protocol/ storage/ndb/memcache/extra/memcached/extensions/protocol/ascii_scrub.c storage/ndb/memcache/extra/memcached/extensions/protocol/example_protocol.c storage/ndb/memcache/extra/memcached/extensions/protocol/fragment_rw.c storage/ndb/memcache/extra/memcached/extensions/protocol/fragment_rw.h storage/ndb/memcache/extra/memcached/extensions/protocol_extension.h storage/ndb/memcache/extra/memcached/include/ storage/ndb/memcache/extra/memcached/include/memcached/ storage/ndb/memcache/extra/memcached/include/memcached/callback.h storage/ndb/memcache/extra/memcached/include/memcached/config_parser.h storage/ndb/memcache/extra/memcached/include/memcached/engine.h storage/ndb/memcache/extra/memcached/include/memcached/engine_common.h storage/ndb/memcache/extra/memcached/include/memcached/engine_testapp.h storage/ndb/memcache/extra/memcached/include/memcached/extension.h storage/ndb/memcache/extra/memcached/include/memcached/extension_loggers.h storage/ndb/memcache/extra/memcached/include/memcached/genhash.h storage/ndb/memcache/extra/memcached/include/memcached/protocol_binary.h storage/ndb/memcache/extra/memcached/include/memcached/protocol_plugin.h storage/ndb/memcache/extra/memcached/include/memcached/server_api.h storage/ndb/memcache/extra/memcached/include/memcached/types.h storage/ndb/memcache/extra/memcached/include/memcached/util.h storage/ndb/memcache/extra/memcached/include/memcached/vbucket.h storage/ndb/memcache/extra/memcached/include/memcached/visibility.h storage/ndb/memcache/extra/memcached/programs/ storage/ndb/memcache/extra/memcached/programs/engine_testapp.c storage/ndb/memcache/extra/memcached/programs/mcstat.c storage/ndb/memcache/extra/memcached/programs/mock_server.c storage/ndb/memcache/extra/memcached/programs/mock_server.h storage/ndb/memcache/extra/memcached/programs/sizes.c storage/ndb/memcache/extra/memcached/programs/testapp.c storage/ndb/memcache/extra/memcached/programs/timedrun.c storage/ndb/memcache/extra/memcached/t/ storage/ndb/memcache/extra/memcached/t/00-startup.t storage/ndb/memcache/extra/memcached/t/64bit.t storage/ndb/memcache/extra/memcached/t/binary-get.t storage/ndb/memcache/extra/memcached/t/binary-sasl.t.in storage/ndb/memcache/extra/memcached/t/binary.t storage/ndb/memcache/extra/memcached/t/bogus-commands.t storage/ndb/memcache/extra/memcached/t/cas.t storage/ndb/memcache/extra/memcached/t/cmd_extensions.t storage/ndb/memcache/extra/memcached/t/daemonize.t storage/ndb/memcache/extra/memcached/t/dash-M.t storage/ndb/memcache/extra/memcached/t/evictions.t storage/ndb/memcache/extra/memcached/t/expirations.t storage/ndb/memcache/extra/memcached/t/flags.t storage/ndb/memcache/extra/memcached/t/flush-all.t storage/ndb/memcache/extra/memcached/t/getset.t storage/ndb/memcache/extra/memcached/t/incrdecr.t storage/ndb/memcache/extra/memcached/t/issue_104.t storage/ndb/memcache/extra/memcached/t/issue_108.t storage/ndb/memcache/extra/memcached/t/issue_14.t storage/ndb/memcache/extra/memcached/t/issue_140.t storage/ndb/memcache/extra/memcached/t/issue_152.t storage/ndb/memcache/extra/memcached/t/issue_163.t storage/ndb/memcache/extra/memcached/t/issue_183.t storage/ndb/memcache/extra/memcached/t/issue_22.t storage/ndb/memcache/extra/memcached/t/issue_29.t storage/ndb/memcache/extra/memcached/t/issue_3.t storage/ndb/memcache/extra/memcached/t/issue_41.t storage/ndb/memcache/extra/memcached/t/issue_42.t storage/ndb/memcache/extra/memcached/t/issue_50.t storage/ndb/memcache/extra/memcached/t/issue_61.t storage/ndb/memcache/extra/memcached/t/issue_67.t storage/ndb/memcache/extra/memcached/t/issue_68.t storage/ndb/memcache/extra/memcached/t/issue_70.t storage/ndb/memcache/extra/memcached/t/item_size_max.t storage/ndb/memcache/extra/memcached/t/lib/ storage/ndb/memcache/extra/memcached/t/lib/MemcachedTest.pm storage/ndb/memcache/extra/memcached/t/line-lengths.t storage/ndb/memcache/extra/memcached/t/lru.t storage/ndb/memcache/extra/memcached/t/maxconns.t storage/ndb/memcache/extra/memcached/t/multiversioning.t storage/ndb/memcache/extra/memcached/t/noreply.t storage/ndb/memcache/extra/memcached/t/sasl/ storage/ndb/memcache/extra/memcached/t/sasl/memcached.conf storage/ndb/memcache/extra/memcached/t/scrub.t storage/ndb/memcache/extra/memcached/t/stats-detail.t storage/ndb/memcache/extra/memcached/t/stats.t storage/ndb/memcache/extra/memcached/t/stress-memcached.pl storage/ndb/memcache/extra/memcached/t/topkeys.t storage/ndb/memcache/extra/memcached/t/udp.t storage/ndb/memcache/extra/memcached/t/unixsocket.t storage/ndb/memcache/extra/memcached/t/verbosity.t storage/ndb/memcache/extra/memcached/t/whitespace.t storage/ndb/memcache/extra/memcached/testsuite/ storage/ndb/memcache/extra/memcached/testsuite/basic_engine_testsuite.c storage/ndb/memcache/extra/memcached/testsuite/basic_engine_testsuite.h storage/ndb/memcache/extra/memcached/testsuite/breakdancer/ storage/ndb/memcache/extra/memcached/testsuite/breakdancer/breakdancer.py storage/ndb/memcache/extra/memcached/testsuite/breakdancer/engine_test.py storage/ndb/memcache/extra/memcached/testsuite/breakdancer/suite_stubs.c storage/ndb/memcache/extra/memcached/testsuite/breakdancer/suite_stubs.h storage/ndb/memcache/extra/memcached/trace.h storage/ndb/memcache/extra/memcached/utilities/ storage/ndb/memcache/extra/memcached/utilities/config_parser.c storage/ndb/memcache/extra/memcached/utilities/engine_loader.c storage/ndb/memcache/extra/memcached/utilities/engine_loader.h storage/ndb/memcache/extra/memcached/utilities/extension_loggers.c storage/ndb/memcache/extra/memcached/utilities/genhash.c storage/ndb/memcache/extra/memcached/utilities/genhash_int.h storage/ndb/memcache/extra/memcached/utilities/util.c storage/ndb/memcache/extra/memcached/win32/ storage/ndb/memcache/extra/memcached/win32/Makefile.mingw storage/ndb/memcache/extra/memcached/win32/config.sh storage/ndb/memcache/extra/memcached/win32/defs.c storage/ndb/memcache/extra/memcached/win32/dlfcn.c storage/ndb/memcache/extra/memcached/win32/dlfcn.h storage/ndb/memcache/extra/memcached/win32/sysexits.h storage/ndb/memcache/extra/memcached/win32/win32.c storage/ndb/memcache/extra/memcached/win32/win32.h storage/ndb/memcache/include/ConnQueryPlanSet.h storage/ndb/memcache/src/ConnQueryPlanSet.cc storage/ndb/memcache/src/stub.cc storage/ndb/src/common/portlib/NdbGetRUsage.cpp storage/ndb/src/kernel/blocks/thrman.cpp storage/ndb/src/kernel/blocks/thrman.hpp storage/ndb/test/rqg/alter_engine.sproc.sql storage/ndb/test/rqg/analyze_db.sproc.sql storage/ndb/test/rqg/copydb.sproc.sql storage/ndb/test/rqg/load_and_run.sh storage/ndb/test/rqg/load_rqg.sh storage/ndb/test/rqg/oj_schema_mod.sproc.sql storage/ndb/test/rqg/oj_schema_mod_ndb.sproc.sql storage/ndb/test/rqg/parseargs.sh storage/ndb/test/rqg/run_rqg.sh storage/ndb/test/run-test/conf-daily-perf.cnf storage/ndb/test/run-test/daily-perf-tests.txt storage/ndb/tools/ndbinfo_select_all.cpp modified: .bzrignore BUILD/compile-pentium-debug-max-no-ndb mysql-test/lib/My/Memcache.pm mysql-test/mysql-test-run.pl mysql-test/suite/binlog/t/binlog_row_mysqlbinlog_db_filter.test mysql-test/suite/innodb/t/disabled.def mysql-test/suite/ndb/r/ndb_alter_table.result mysql-test/suite/ndb/r/ndb_basic.result mysql-test/suite/ndb/r/ndb_index_stat.result mysql-test/suite/ndb/r/ndbinfo.result mysql-test/suite/ndb/t/ndb_alter_table.test mysql-test/suite/ndb_big/my.cnf mysql-test/suite/ndb_memcache/include/datatypes_tables.inc mysql-test/suite/ndb_memcache/include/memcached_wait_for_ready.inc mysql-test/suite/ndb_memcache/t/reconf1.test mysql-test/suite/ndb_memcache/t/type_char.test mysql-test/suite/ndb_memcache/t/type_signed.test mysql-test/suite/ndb_memcache/t/type_unsigned.test sql/ha_ndb_index_stat.cc sql/ha_ndb_index_stat.h sql/ha_ndbcluster.cc storage/ndb/compile-cluster storage/ndb/include/kernel/BlockNumbers.h storage/ndb/include/kernel/ndb_limits.h storage/ndb/include/kernel/signaldata/SchemaTrans.hpp storage/ndb/include/mgmapi/mgmapi_config_parameters.h storage/ndb/include/ndb_constants.h storage/ndb/memcache/CMakeLists.txt storage/ndb/memcache/FindMemcached.cmake storage/ndb/memcache/atomics.cmake storage/ndb/memcache/include/Config_v1.h storage/ndb/memcache/include/Configuration.h storage/ndb/memcache/include/DataTypeHandler.h storage/ndb/memcache/include/NdbInstance.h storage/ndb/memcache/include/Operation.h storage/ndb/memcache/include/QueryPlan.h storage/ndb/memcache/include/Record.h storage/ndb/memcache/include/Scheduler.h storage/ndb/memcache/include/TabSeparatedValues.h storage/ndb/memcache/include/TableSpec.h storage/ndb/memcache/include/atomics.h storage/ndb/memcache/include/debug.h storage/ndb/memcache/include/ndbmemcache_config.in storage/ndb/memcache/include/workitem.h storage/ndb/memcache/memcached_path.pl.in storage/ndb/memcache/sandbox.sh.in storage/ndb/memcache/src/ClusterConnectionPool.cc storage/ndb/memcache/src/Config_v1.cc storage/ndb/memcache/src/Configuration.cc storage/ndb/memcache/src/DataTypeHandler.cc storage/ndb/memcache/src/NdbInstance.cc storage/ndb/memcache/src/Operation.cc storage/ndb/memcache/src/QueryPlan.cc storage/ndb/memcache/src/Record.cc storage/ndb/memcache/src/TabSeparatedValues.cc storage/ndb/memcache/src/TableSpec.cc storage/ndb/memcache/src/ndb_configuration.cc storage/ndb/memcache/src/ndb_engine.c storage/ndb/memcache/src/ndb_engine_private.h storage/ndb/memcache/src/ndb_pipeline.cc storage/ndb/memcache/src/ndb_worker.cc storage/ndb/memcache/src/schedulers/S_sched.cc storage/ndb/memcache/src/schedulers/S_sched.h storage/ndb/memcache/src/schedulers/Stockholm.cc storage/ndb/memcache/src/schedulers/Stockholm.h storage/ndb/memcache/src/workitem.c storage/ndb/memcache/unit/CMakeLists.txt storage/ndb/memcache/unit/all_tests.h storage/ndb/memcache/unit/alloc.cc storage/ndb/memcache/unit/cas.cc storage/ndb/memcache/unit/casbits.cc storage/ndb/memcache/unit/connpool.cc storage/ndb/memcache/unit/harness.cc storage/ndb/memcache/unit/incr.cc storage/ndb/memcache/unit/queue.cc storage/ndb/memcache/unit/stub_logger.c storage/ndb/memcache/unit/test_workqueue.c storage/ndb/memcache/unit/tsv.cc storage/ndb/src/common/debugger/BlockNames.cpp storage/ndb/src/common/portlib/CMakeLists.txt storage/ndb/src/common/portlib/NdbThread.c storage/ndb/src/kernel/SimBlockList.cpp storage/ndb/src/kernel/blocks/CMakeLists.txt storage/ndb/src/kernel/blocks/LocalProxy.cpp storage/ndb/src/kernel/blocks/LocalProxy.hpp storage/ndb/src/kernel/blocks/PgmanProxy.cpp storage/ndb/src/kernel/blocks/PgmanProxy.hpp storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp storage/ndb/src/kernel/blocks/dbinfo/Dbinfo.cpp storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp storage/ndb/src/kernel/blocks/dblqh/DblqhProxy.cpp storage/ndb/src/kernel/blocks/dbspj/DbspjProxy.hpp storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp storage/ndb/src/kernel/blocks/dbtc/DbtcProxy.hpp storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp storage/ndb/src/kernel/blocks/record_types.hpp storage/ndb/src/kernel/blocks/tsman.cpp storage/ndb/src/kernel/ndbd.cpp storage/ndb/src/kernel/vm/DLFifoList.hpp storage/ndb/src/kernel/vm/DLHashTable.hpp storage/ndb/src/kernel/vm/DataBuffer2.hpp storage/ndb/src/kernel/vm/Ndbinfo.hpp storage/ndb/src/kernel/vm/NdbinfoTables.cpp storage/ndb/src/kernel/vm/Pool.hpp storage/ndb/src/kernel/vm/SimulatedBlock.hpp storage/ndb/src/kernel/vm/dummy_nonmt.cpp storage/ndb/src/kernel/vm/mt.cpp storage/ndb/src/kernel/vm/mt.hpp storage/ndb/src/kernel/vm/mt_thr_config.cpp storage/ndb/src/kernel/vm/mt_thr_config.hpp storage/ndb/src/kernel/vm/ndbd_malloc_impl.hpp storage/ndb/src/mgmsrv/ConfigInfo.cpp storage/ndb/src/ndbapi/TransporterFacade.cpp storage/ndb/src/ndbapi/ndberror.c storage/ndb/test/include/HugoCalculator.hpp storage/ndb/test/include/HugoOperations.hpp storage/ndb/test/include/HugoQueryBuilder.hpp storage/ndb/test/ndbapi/testNdbApi.cpp storage/ndb/test/run-test/atrt.hpp storage/ndb/test/run-test/daily-basic-tests.txt storage/ndb/test/run-test/daily-devel-tests.txt storage/ndb/test/run-test/files.cpp storage/ndb/test/run-test/main.cpp storage/ndb/test/run-test/setup.cpp storage/ndb/test/src/HugoOperations.cpp storage/ndb/tools/CMakeLists.txt storage/ndb/tools/ndbinfo_sql.cpp support-files/compiler_warnings.supp === modified file 'mysql-test/suite/ndb/r/ndb_join_pushdown.result' --- a/mysql-test/suite/ndb/r/ndb_join_pushdown.result 2011-10-05 07:24:39 +0000 +++ b/mysql-test/suite/ndb/r/ndb_join_pushdown.result 2011-10-18 10:43:35 +0000 @@ -4028,15 +4028,15 @@ and new_count.counter_name <> 'LOCAL_REA and new_count.counter_name <> 'REMOTE_READS_SENT'; counter_name new_count.val - old_count.val CONST_PRUNED_RANGE_SCANS_RECEIVED 0 -LOCAL_RANGE_SCANS_SENT 2 +LOCAL_RANGE_SCANS_SENT 0 LOCAL_TABLE_SCANS_SENT 2 PRUNED_RANGE_SCANS_RECEIVED 0 -RANGE_SCANS_RECEIVED 2 -READS_NOT_FOUND 2 +RANGE_SCANS_RECEIVED 0 +READS_NOT_FOUND 1 READS_RECEIVED 1 REMOTE_RANGE_SCANS_SENT 0 -SCAN_BATCHES_RETURNED 4 -SCAN_ROWS_RETURNED 8 +SCAN_BATCHES_RETURNED 2 +SCAN_ROWS_RETURNED 5 TABLE_SCANS_RECEIVED 2 select 'READS_SENT', sum(new_count.val - old_count.val) from new_count, old_count @@ -4044,7 +4044,7 @@ where new_count.counter_name = old_count and (new_count.counter_name = 'LOCAL_READS_SENT' or new_count.counter_name = 'REMOTE_READS_SENT'); READS_SENT sum(new_count.val - old_count.val) -READS_SENT 7 +READS_SENT 5 drop table old_count; drop table new_count; drop table t1; @@ -4191,7 +4191,7 @@ select count(*) from t1 t1, t1 t2 where count(*) 1 pruned_scan_count -2 +0 explain extended select count(*) from t1 t1, t1 t2 where t1.a = 10 and (t1.b<11 or t1.b>11) and t2.a = t1.c and t2.b = t1.c; id select_type table type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 range PRIMARY,i2 i2 8 NULL 6 100.00 Parent of 2 pushed join@1; Using where with pushed condition: ((`test`.`t1`.`a` = 10) and ((`test`.`t1`.`b` < 11) or (`test`.`t1`.`b` > 11))) @@ -4202,7 +4202,7 @@ select count(*) from t1 t1, t1 t2 where count(*) 1 pruned_scan_count -2 +1 drop table t1; create table t2( d int not null, @@ -5487,21 +5487,21 @@ counter_name spj_counts_at_end.val - spj CONST_PRUNED_RANGE_SCANS_RECEIVED 6 LOCAL_TABLE_SCANS_SENT 250 PRUNED_RANGE_SCANS_RECEIVED 25 -RANGE_SCANS_RECEIVED 733 -READS_RECEIVED 58 +RANGE_SCANS_RECEIVED 703 +READS_RECEIVED 53 TABLE_SCANS_RECEIVED 250 drop table spj_counts_at_startup; drop table spj_counts_at_end; scan_count_derived 1.0 pruned_scan_count -11 +8 sorted_scan_count -40 +34 pushed_queries_defined 401 pushed_queries_dropped -12 +6 pushed_queries_executed -552 +529 set ndb_join_pushdown = @save_ndb_join_pushdown; === modified file 'mysql-test/suite/ndb/r/ndb_read_multi_range.result' --- a/mysql-test/suite/ndb/r/ndb_read_multi_range.result 2011-09-05 14:12:11 +0000 +++ b/mysql-test/suite/ndb/r/ndb_read_multi_range.result 2011-10-18 10:43:35 +0000 @@ -602,6 +602,7 @@ t1.a + t2.a*10 + t3.a*100 + t4.a*1000, from t2 as t1, t2 as t2, t2 as t3, t2 as t4 where (t1.a + t2.a*10 + t3.a*100 + t4.a*1000) < 3000; +set optimizer_switch='block_nested_loop=off'; explain SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; @@ -610,4 +611,5 @@ id select_type table type possible_keys 1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 6 Using where; Distinct SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; +set optimizer_switch='block_nested_loop=default'; drop table t1, t2; === modified file 'mysql-test/suite/ndb/t/ndb_read_multi_range.test' --- a/mysql-test/suite/ndb/t/ndb_read_multi_range.test 2011-09-05 14:12:11 +0000 +++ b/mysql-test/suite/ndb/t/ndb_read_multi_range.test 2011-10-18 10:43:35 +0000 @@ -480,6 +480,7 @@ where (t1.a + t2.a*10 + t3.a*100 + t4.a* # when the first matching 't2.a = t1.a' is found. # - 'LEFT JOIN' is to ensure that 'Using join buffer' is *not* used # +set optimizer_switch='block_nested_loop=off'; explain SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; @@ -489,4 +490,5 @@ SELECT DISTINCT STRAIGHT_JOIN t1.pk FROM t1 LEFT JOIN t2 ON t2.a = t1.a AND t2.pk != 6; --enable_result_log +set optimizer_switch='block_nested_loop=default'; drop table t1, t2; === modified file 'sql/ha_ndbcluster.cc' --- a/sql/ha_ndbcluster.cc 2011-10-17 13:30:56 +0000 +++ b/sql/ha_ndbcluster.cc 2011-10-18 10:43:35 +0000 @@ -13261,9 +13261,6 @@ ulonglong ha_ndbcluster::table_flags(voi HA_NULL_IN_KEY | HA_AUTO_PART_KEY | HA_NO_PREFIX_CHAR_KEYS | -#ifndef NDB_WITH_NEW_MRR_INTERFACE - HA_NEED_READ_RANGE_BUFFER | -#endif HA_CAN_GEOMETRY | HA_CAN_BIT_FIELD | HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | @@ -14509,7 +14506,912 @@ void ha_ndbcluster::check_read_before_wr DBUG_VOID_RETURN; } -#ifndef NDB_WITH_NEW_MRR_INTERFACE +#ifdef NDB_WITH_NEW_MRR_INTERFACE +/**************************************************************************** + * MRR interface implementation + ***************************************************************************/ + + +/** + We will not attempt to deal with more than this many ranges in a single + MRR execute(). +*/ +#define MRR_MAX_RANGES 128 + +/* + Types of ranges during multi_range_read. + + Code assumes that X < enum_ordered_range is a valid check for range converted + to key operation. +*/ +enum multi_range_types +{ + enum_unique_range, /// Range converted to key operation + enum_empty_unique_range, /// No data found (in key operation) + enum_ordered_range, /// Normal ordered index scan range + enum_skip_range /// Empty range (eg. partition pruning) +}; + +/** + Usage of the MRR buffer is as follows: + + First, N char * values, each being the custom value obtained from + RANGE_SEQ_IF::next() that needs to be returned from multi_range_read_next(). + N is usually == total number of ranges, but never more than MRR_MAX_RANGES + (the MRR is split across several execute()s if necessary). N may be lower + than actual number of ranges in a single execute() in case of split for + other reasons. + + This is followed by N variable-sized entries, each + + - 1 byte of multi_range_types for this range. + + - (Only) for ranges converted to key operations (enum_unique_range and + enum_empty_unique_range), this is followed by table_share->reclength + bytes of row data. +*/ + +/* Return the needed size of the fixed array at start of HANDLER_BUFFER. */ +static ulong +multi_range_fixed_size(int num_ranges) +{ + if (num_ranges > MRR_MAX_RANGES) + num_ranges= MRR_MAX_RANGES; + return num_ranges * sizeof(char *); +} + +/* Return max number of ranges so that fixed part will still fit in buffer. */ +static int +multi_range_max_ranges(int num_ranges, ulong bufsize) +{ + if (num_ranges > MRR_MAX_RANGES) + num_ranges= MRR_MAX_RANGES; + if (num_ranges * sizeof(char *) > bufsize) + num_ranges= bufsize / sizeof(char *); + return num_ranges; +} + +/* Return the size in HANDLER_BUFFER of a variable-sized entry. */ +static ulong +multi_range_entry_size(my_bool use_keyop, ulong reclength) +{ + /* Space for type byte. */ + ulong len= 1; + if (use_keyop) + len+= reclength; + return len; +} + +/* + Return the maximum size of a variable-sized entry in HANDLER_BUFFER. + + Actual size may depend on key values (whether the actual value can be + converted to a hash key operation or needs to be done as an ordered index + scan). +*/ +static ulong +multi_range_max_entry(NDB_INDEX_TYPE keytype, ulong reclength) +{ + return multi_range_entry_size(keytype != ORDERED_INDEX, reclength); +} + +static uchar & +multi_range_entry_type(uchar *p) +{ + return *p; +} + +/* Find the start of the next entry in HANDLER_BUFFER. */ +static uchar * +multi_range_next_entry(uchar *p, ulong reclength) +{ + my_bool use_keyop= multi_range_entry_type(p) < enum_ordered_range; + return p + multi_range_entry_size(use_keyop, reclength); +} + +/* Get pointer to row data (for range converted to key operation). */ +static uchar * +multi_range_row(uchar *p) +{ + DBUG_ASSERT(multi_range_entry_type(p) == enum_unique_range); + return p + 1; +} + +/* Get and put upper layer custom char *, use memcpy() for unaligned access. */ +static char * +multi_range_get_custom(HANDLER_BUFFER *buffer, int range_no) +{ + DBUG_ASSERT(range_no < MRR_MAX_RANGES); + return ((char **)(buffer->buffer))[range_no]; +} + +static void +multi_range_put_custom(HANDLER_BUFFER *buffer, int range_no, char *custom) +{ + DBUG_ASSERT(range_no < MRR_MAX_RANGES); + ((char **)(buffer->buffer))[range_no]= custom; +} + +/* + This is used to check if an ordered index scan is needed for a range in + a multi range read. + If a scan is not needed, we use a faster primary/unique key operation + instead. +*/ +static my_bool +read_multi_needs_scan(NDB_INDEX_TYPE cur_index_type, const KEY *key_info, + const KEY_MULTI_RANGE *r) +{ + if (cur_index_type == ORDERED_INDEX) + return TRUE; + if (cur_index_type == PRIMARY_KEY_INDEX || + cur_index_type == UNIQUE_INDEX) + return FALSE; + DBUG_ASSERT(cur_index_type == PRIMARY_KEY_ORDERED_INDEX || + cur_index_type == UNIQUE_ORDERED_INDEX); + if (r->start_key.length != key_info->key_length || + r->start_key.flag != HA_READ_KEY_EXACT) + return TRUE; // Not exact match, need scan + if (cur_index_type == UNIQUE_ORDERED_INDEX && + check_null_in_key(key_info, r->start_key.key,r->start_key.length)) + return TRUE; // Can't use for NULL values + return FALSE; +} + +/* + Get cost and other information about MRR scan over a known list of ranges + + SYNOPSIS + See handler::multi_range_read_info_const. + + DESCRIPTION + The implementation is copied from handler::multi_range_read_info_const. + The only difference is that NDB-MRR cannot handle blob columns or keys + with NULLs for unique indexes. We disable MRR for those cases. + As Pushed join execution has not yet been integrated into NDB-MRR, + we also disable MRR for those. + + NOTES + See NOTES for handler::multi_range_read_info_const(). +*/ + +ha_rows +ha_ndbcluster::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges_arg, uint *bufsz, + uint *flags, COST_VECT *cost) +{ + KEY_MULTI_RANGE range; + range_seq_t seq_it; + ha_rows rows, total_rows= 0; + uint n_ranges=0; + bool null_ranges= FALSE; + THD *thd= current_thd; + NDB_INDEX_TYPE key_type= get_index_type(keyno); + KEY* key_info= table->key_info + keyno; + ulong reclength= table_share->reclength; + ulong total_bufsize; + uint save_bufsize= *bufsz; + DBUG_ENTER("ha_ndbcluster::multi_range_read_info_const"); + + total_bufsize= multi_range_fixed_size(n_ranges_arg); + + seq_it= seq->init(seq_init_param, n_ranges, *flags); + while (!seq->next(seq_it, &range)) + { + if (unlikely(thd->killed != 0)) + DBUG_RETURN(HA_POS_ERROR); + + n_ranges++; + key_range *min_endp= range.start_key.length? &range.start_key : NULL; + key_range *max_endp= range.end_key.length? &range.end_key : NULL; + null_ranges|= (range.range_flag & NULL_RANGE); + if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE)) + rows= 1; /* there can be at most one row */ + else + { + if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp, + max_endp))) + { + /* Can't scan one range => can't do MRR scan at all */ + total_rows= HA_POS_ERROR; + break; + } + } + total_rows+= rows; + total_bufsize+= + multi_range_max_entry((read_multi_needs_scan(key_type, key_info, &range) ? + ORDERED_INDEX : + UNIQUE_INDEX), + reclength); + } + + if (total_rows != HA_POS_ERROR) + { + if (uses_blob_value(table->read_set) || + ((get_index_type(keyno) == UNIQUE_INDEX && + has_null_in_unique_index(keyno)) && null_ranges) + || (m_pushed_join_operation==PUSHED_ROOT && + !m_disable_pushed_join)) + { + /* Use default MRR implementation */ + *flags|= HA_MRR_USE_DEFAULT_IMPL; + *bufsz= 0; + } + else + { + total_bufsize+= multi_range_fixed_size(total_rows); + + DBUG_PRINT("info", ("MRR bufsize suggested=%u want=%lu limit=%d", + save_bufsize, total_bufsize, + (*flags & HA_MRR_LIMITS) != 0)); + + if (unlikely(total_bufsize > (ulong)UINT_MAX)) + total_bufsize= (ulong)UINT_MAX; + + /* + We'll be most efficient when we have buffer big enough to accomodate + all ranges. But we need at least sufficient buffer for one range to + do MRR at all. + */ + uint entry_size= multi_range_max_entry(key_type, reclength); + uint min_total_size= entry_size + multi_range_fixed_size(1); + if (save_bufsize < min_total_size) + { + if(*flags & HA_MRR_LIMITS) + { + /* Too small buffer limit to do MRR. */ + *flags|= HA_MRR_USE_DEFAULT_IMPL; + *bufsz= 0; + } + else + { + *flags&= ~HA_MRR_USE_DEFAULT_IMPL; + *bufsz= min_total_size; + } + } + else + { + *flags&= ~HA_MRR_USE_DEFAULT_IMPL; + *bufsz= min(save_bufsize, total_bufsize); + } + } + DBUG_PRINT("info", ("MRR bufsize set to %u", *bufsz)); + cost->zero(); + cost->avg_io_cost= 1; /* assume random seeks */ + if ((*flags & HA_MRR_INDEX_ONLY) && total_rows > 2) + cost->io_count= index_only_read_time(keyno, total_rows); + else + cost->io_count= read_time(keyno, n_ranges, total_rows); + cost->cpu_cost= total_rows * ROW_EVALUATE_COST + 0.01; + } + DBUG_RETURN(total_rows); +} + + +/* + Get cost and other information about MRR scan over some sequence of ranges + + SYNOPSIS + See handler::multi_range_read_info. +*/ + +ha_rows +ha_ndbcluster::multi_range_read_info(uint keyno, uint n_ranges, uint keys, + uint *bufsz, uint *flags, COST_VECT *cost) +{ + ha_rows res; + uint save_bufsize= *bufsz; + DBUG_ENTER("ha_ndbcluster::multi_range_read_info"); + + res= handler::multi_range_read_info(keyno, n_ranges, keys, bufsz, flags, + cost); + NDB_INDEX_TYPE key_type= get_index_type(keyno); + /* Disable MRR on blob read and on NULL lookup in unique index. */ + if (uses_blob_value(table->read_set) || + ( key_type == UNIQUE_INDEX && + has_null_in_unique_index(keyno) && + !(*flags & HA_MRR_NO_NULL_ENDPOINTS))) + { + *flags|= HA_MRR_USE_DEFAULT_IMPL; + *bufsz= 0; + } + else + { + ulong reclength= table_share->reclength; + uint entry_size= multi_range_max_entry(key_type, reclength); + uint min_total_size= entry_size + multi_range_fixed_size(1); + DBUG_PRINT("info", ("MRR bufsize suggested=%u want=%u limit=%d", + save_bufsize, (keys + 1) * entry_size, + (*flags & HA_MRR_LIMITS) != 0)); + if (save_bufsize < min_total_size) + { + if(*flags & HA_MRR_LIMITS) + { + /* Too small buffer limit to do MRR. */ + *flags|= HA_MRR_USE_DEFAULT_IMPL; + *bufsz= 0; + } + else + { + *flags&= ~HA_MRR_USE_DEFAULT_IMPL; + *bufsz= min_total_size; + } + } + else + { + *flags&= ~HA_MRR_USE_DEFAULT_IMPL; + *bufsz= min(save_bufsize, + keys * entry_size + multi_range_fixed_size(n_ranges)); + } + DBUG_PRINT("info", ("MRR bufsize set to %u", *bufsz)); + } + DBUG_RETURN(res); +} + +int ha_ndbcluster::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, + void *seq_init_param, + uint n_ranges, uint mode, + HANDLER_BUFFER *buffer) +{ + int error; + DBUG_ENTER("ha_ndbcluster::multi_range_read_init"); + + /* + If supplied buffer is smaller than needed for just one range, we cannot do + multi_range_read. + */ + ulong bufsize= buffer->buffer_end - buffer->buffer; + + if (mode & HA_MRR_USE_DEFAULT_IMPL + || bufsize < multi_range_fixed_size(1) + + multi_range_max_entry(get_index_type(active_index), + table_share->reclength) + || m_delete_cannot_batch || m_update_cannot_batch) + { + m_disable_multi_read= TRUE; + DBUG_RETURN(handler::multi_range_read_init(seq_funcs, seq_init_param, + n_ranges, mode, buffer)); + } + + /** + * There may still be an open m_multi_cursor from the previous mrr access on this handler. + * Close it now to free up resources for this NdbScanOperation. + */ + if (unlikely((error= close_scan()))) + DBUG_RETURN(error); + + m_disable_multi_read= FALSE; + + mrr_is_output_sorted= test(mode & HA_MRR_SORTED); + /* + Copy arguments into member variables + */ + multi_range_buffer= buffer; + mrr_funcs= *seq_funcs; + mrr_iter= mrr_funcs.init(seq_init_param, n_ranges, mode); + ranges_in_seq= n_ranges; + m_range_res= mrr_funcs.next(mrr_iter, &mrr_cur_range); + mrr_need_range_assoc = !test(mode & HA_MRR_NO_ASSOCIATION); + if (mrr_need_range_assoc) + { + ha_statistic_increment(&SSV::ha_multi_range_read_init_count); + } + + /* + We do not start fetching here with execute(), rather we defer this to the + first call to multi_range_read_next() by setting first_running_range and + first_unstarted_range like this. + + The reason is that the MRR interface is designed so that in some cases + multi_range_read_next() may never get called (eg. in case of WHERE + condition on previous table that is never satisfied). So we may not need + to fetch anything. + + Also, at the time of writing, returning an error from + multi_range_read_init() does not correctly set the error status, so we get + an assert on missing result status in net_end_statement(). + */ + first_running_range= 0; + first_unstarted_range= 0; + + DBUG_RETURN(0); +} + + +int ha_ndbcluster::multi_range_start_retrievals(uint starting_range) +{ + KEY* key_info= table->key_info + active_index; + ulong reclength= table_share->reclength; + const NdbOperation* op; + NDB_INDEX_TYPE cur_index_type= get_index_type(active_index); + const NdbOperation *oplist[MRR_MAX_RANGES]; + uint num_keyops= 0; + NdbTransaction *trans= m_thd_ndb->trans; + int error; + + DBUG_ENTER("multi_range_start_retrievals"); + + /* + * read multi range will read ranges as follows (if not ordered) + * + * input read order + * ====== ========== + * pk-op 1 pk-op 1 + * pk-op 2 pk-op 2 + * range 3 range (3,5) NOTE result rows will be intermixed + * pk-op 4 pk-op 4 + * range 5 + * pk-op 6 pk-op 6 + */ + + /* + We loop over all ranges, converting into primary/unique key operations if + possible, and adding ranges to an ordered index scan for the rest. + + If the supplied HANDLER_BUFFER is too small, we may also need to do only + part of the multi read at once. + */ + + DBUG_ASSERT(cur_index_type != UNDEFINED_INDEX); + DBUG_ASSERT(m_multi_cursor==NULL); + DBUG_ASSERT(m_active_query==NULL); + + const NdbOperation::LockMode lm = get_ndb_lock_mode(m_lock.type); + const uchar *end_of_buffer= multi_range_buffer->buffer_end; + + /* + Normally we should have sufficient buffer for the whole fixed_sized part. + But we need to make sure we do not crash if upper layer gave us a _really_ + small buffer. + + We already checked (in multi_range_read_init()) that we got enough buffer + for at least one range. + */ + uint min_entry_size= + multi_range_entry_size(!read_multi_needs_scan(cur_index_type, key_info, + &mrr_cur_range), reclength); + ulong bufsize= end_of_buffer - multi_range_buffer->buffer; + int max_range= multi_range_max_ranges(ranges_in_seq, + bufsize - min_entry_size); + DBUG_ASSERT(max_range > 0); + uchar *row_buf= multi_range_buffer->buffer + multi_range_fixed_size(max_range); + m_multi_range_result_ptr= row_buf; + + int range_no= 0; + int mrr_range_no= starting_range; + + for (; + !m_range_res; + range_no++, m_range_res= mrr_funcs.next(mrr_iter, &mrr_cur_range)) + { + if (range_no >= max_range) + break; + my_bool need_scan= + read_multi_needs_scan(cur_index_type, key_info, &mrr_cur_range); + if (row_buf + multi_range_entry_size(!need_scan, reclength) > end_of_buffer) + break; + if (need_scan) + { + if (range_no > NdbIndexScanOperation::MaxRangeNo) + break; + /* + Check how much KEYINFO data we already used for index bounds, and + split the MRR here if it exceeds a certain limit. This way we avoid + overloading the TC block in the ndb kernel. + + The limit used is based on the value MAX_KEY_SIZE_IN_WORDS. + */ + if (m_multi_cursor && m_multi_cursor->getCurrentKeySize() >= 1000) + break; + } + + mrr_range_no++; + multi_range_put_custom(multi_range_buffer, range_no, mrr_cur_range.ptr); + + part_id_range part_spec; + if (m_use_partition_pruning) + { + get_partition_set(table, table->record[0], active_index, + &mrr_cur_range.start_key, + &part_spec); + DBUG_PRINT("info", ("part_spec.start_part: %u part_spec.end_part: %u", + part_spec.start_part, part_spec.end_part)); + /* + If partition pruning has found no partition in set + we can skip this scan + */ + if (part_spec.start_part > part_spec.end_part) + { + /* + We can skip this partition since the key won't fit into any + partition + */ + multi_range_entry_type(row_buf)= enum_skip_range; + row_buf= multi_range_next_entry(row_buf, reclength); + continue; + } + if (!trans && + (part_spec.start_part == part_spec.end_part)) + if (unlikely(!(trans= start_transaction_part_id(part_spec.start_part, + error)))) + DBUG_RETURN(error); + } + + if (need_scan) + { + if (!trans) + { + // ToDo see if we can use start_transaction_key here instead + if (!m_use_partition_pruning) + { + get_partition_set(table, table->record[0], active_index, + &mrr_cur_range.start_key, + &part_spec); + if (part_spec.start_part == part_spec.end_part) + { + if (unlikely(!(trans= start_transaction_part_id(part_spec.start_part, + error)))) + DBUG_RETURN(error); + } + else if (unlikely(!(trans= start_transaction(error)))) + DBUG_RETURN(error); + } + else if (unlikely(!(trans= start_transaction(error)))) + DBUG_RETURN(error); + } + + /* Create the scan operation for the first scan range. */ + if (!m_multi_cursor) + { + /* Do a multi-range index scan for ranges not done by primary/unique key. */ + NdbScanOperation::ScanOptions options; + NdbInterpretedCode code(m_table); + + options.optionsPresent= + NdbScanOperation::ScanOptions::SO_SCANFLAGS | + NdbScanOperation::ScanOptions::SO_PARALLEL; + + options.scan_flags= + NdbScanOperation::SF_ReadRangeNo | + NdbScanOperation::SF_MultiRange; + + if (lm == NdbOperation::LM_Read) + options.scan_flags|= NdbScanOperation::SF_KeyInfo; + if (mrr_is_output_sorted) + options.scan_flags|= NdbScanOperation::SF_OrderByFull; + + options.parallel= DEFAULT_PARALLELISM; + + NdbOperation::GetValueSpec gets[2]; + if (table_share->primary_key == MAX_KEY) + get_hidden_fields_scan(&options, gets); + + if (m_cond && m_cond->generate_scan_filter(&code, &options)) + ERR_RETURN(code.getNdbError()); + + /* Define scan */ + NdbIndexScanOperation *scanOp= trans->scanIndex + (m_index[active_index].ndb_record_key, + m_ndb_record, + lm, + (uchar *)(table->read_set->bitmap), + NULL, /* All bounds specified below */ + &options, + sizeof(NdbScanOperation::ScanOptions)); + + if (!scanOp) + ERR_RETURN(trans->getNdbError()); + + m_multi_cursor= scanOp; + + /* + We do not get_blob_values() here, as when using blobs we always + fallback to non-batched multi range read (see multi_range_read_info + function). + */ + + /* We set m_next_row=0 to say that no row was fetched from the scan yet. */ + m_next_row= 0; + } + + Ndb::PartitionSpec ndbPartitionSpec; + const Ndb::PartitionSpec* ndbPartSpecPtr= NULL; + + /* If this table uses user-defined partitioning, use MySQLD provided + * partition info as pruning info + * Otherwise, scan range pruning is performed automatically by + * NDBAPI based on distribution key values. + */ + if (m_use_partition_pruning && + m_user_defined_partitioning && + (part_spec.start_part == part_spec.end_part)) + { + DBUG_PRINT("info", ("Range on user-def-partitioned table can be pruned to part %u", + part_spec.start_part)); + ndbPartitionSpec.type= Ndb::PartitionSpec::PS_USER_DEFINED; + ndbPartitionSpec.UserDefined.partitionId= part_spec.start_part; + ndbPartSpecPtr= &ndbPartitionSpec; + } + + /* Include this range in the ordered index scan. */ + NdbIndexScanOperation::IndexBound bound; + compute_index_bounds(bound, key_info, + &mrr_cur_range.start_key, &mrr_cur_range.end_key, 0); + bound.range_no= range_no; + + if (m_multi_cursor->setBound(m_index[active_index].ndb_record_key, + bound, + ndbPartSpecPtr, // Only for user-def tables + sizeof(Ndb::PartitionSpec))) + { + ERR_RETURN(trans->getNdbError()); + } + + multi_range_entry_type(row_buf)= enum_ordered_range; + row_buf= multi_range_next_entry(row_buf, reclength); + } + else + { + if (!trans) + { + DBUG_ASSERT(active_index != MAX_KEY); + if (unlikely(!(trans= start_transaction_key(active_index, + mrr_cur_range.start_key.key, + error)))) + DBUG_RETURN(error); + } + /* Convert to primary/unique key operation. */ + Uint32 partitionId; + Uint32* ppartitionId = NULL; + + if (m_user_defined_partitioning && + (cur_index_type == PRIMARY_KEY_ORDERED_INDEX || + cur_index_type == PRIMARY_KEY_INDEX)) + { + partitionId=part_spec.start_part; + ppartitionId=&partitionId; + } + + multi_range_entry_type(row_buf)= enum_unique_range; + if (!(op= pk_unique_index_read_key(active_index, + mrr_cur_range.start_key.key, + multi_range_row(row_buf), lm, + ppartitionId))) + ERR_RETURN(trans->getNdbError()); + oplist[num_keyops++]= op; + row_buf= multi_range_next_entry(row_buf, reclength); + } + } + + if (m_multi_cursor) + { + DBUG_PRINT("info", ("Is MRR scan pruned to 1 partition? :%u", + m_multi_cursor->getPruned())); + m_thd_ndb->m_scan_count++; + m_thd_ndb->m_pruned_scan_count += (m_multi_cursor->getPruned()? 1 : 0); + if (mrr_is_output_sorted) + { + m_thd_ndb->m_sorted_scan_count++; + } + } + + if (execute_no_commit_ie(m_thd_ndb, trans)) + ERR_RETURN(trans->getNdbError()); + + if (!m_range_res) + { + DBUG_PRINT("info", + ("Split MRR read, %d-%d of %d bufsize=%lu used=%lu range_no=%d", + starting_range, mrr_range_no - 1, ranges_in_seq, + (ulong)(end_of_buffer - multi_range_buffer->buffer), + (ulong)(row_buf - multi_range_buffer->buffer), range_no)); + /* + Mark that we're using entire buffer (even if might not) as we are not + reading read all ranges yet. + + This as we don't want mysqld to reuse the buffer when we read the + remaining ranges. + */ + multi_range_buffer->end_of_used_area= multi_range_buffer->buffer_end; + } + else + multi_range_buffer->end_of_used_area= row_buf; + + first_running_range= first_range_in_batch= starting_range; + first_unstarted_range= mrr_range_no; + m_current_range_no= 0; + + /* + Now we need to inspect all ranges that were converted to key operations. + + We need to check for any error (in particular NoDataFound), and remember + the status, since the operation pointer may no longer be valid when we + actually get to it in multi_range_next_entry() (we may have done further + execute()'s in a different handler object during joins eg.) + */ + row_buf= m_multi_range_result_ptr; + uint op_idx= 0; + for (uint r= first_range_in_batch; r < first_unstarted_range; r++) + { + uchar &type_loc= multi_range_entry_type(row_buf); + row_buf= multi_range_next_entry(row_buf, reclength); + if (type_loc >= enum_ordered_range) + continue; + + DBUG_ASSERT(op_idx < MRR_MAX_RANGES); + const NdbError &error= oplist[op_idx]->getNdbError(); + if (error.code != 0) + { + if (error.classification == NdbError::NoDataFound) + type_loc= enum_empty_unique_range; + else + { + /* + This shouldn't really happen. + + There aren't really any other errors that could happen on the read + without also aborting the transaction and causing execute() to + return failure. + + (But we can still safely return an error code in non-debug builds). + */ + DBUG_ASSERT(FALSE); + ERR_RETURN(error); /* purecov: deadcode */ + } + } + op_idx++; + } + + DBUG_RETURN(0); +} + +int ha_ndbcluster::multi_range_read_next(char **range_info) +{ + int res; + DBUG_ENTER("ha_ndbcluster::multi_range_read_next"); + + if (m_disable_multi_read) + { + DBUG_RETURN(handler::multi_range_read_next(range_info)); + } + + for(;;) + { + + /* for each range (we should have remembered the number) */ + while (first_running_range < first_unstarted_range) + { + uchar *row_buf= m_multi_range_result_ptr; + int expected_range_no= first_running_range - first_range_in_batch; + + switch (multi_range_entry_type(row_buf)) + { + case enum_skip_range: + case enum_empty_unique_range: + /* Nothing in this range; continue with next. */ + break; + + case enum_unique_range: + /* + Move to next range; we can have at most one record from a unique + range. + */ + first_running_range++; + m_multi_range_result_ptr= + multi_range_next_entry(m_multi_range_result_ptr, + table_share->reclength); + + /* + Clear m_active_cursor; it is used as a flag in update_row() / + delete_row() to know whether the current tuple is from a scan + or pk operation. + */ + m_active_cursor= NULL; + + /* Return the record. */ + *range_info= multi_range_get_custom(multi_range_buffer, + expected_range_no); + memcpy(table->record[0], multi_range_row(row_buf), + table_share->reclength); + DBUG_RETURN(0); + + case enum_ordered_range: + /* An index scan range. */ + { + int res; + if ((res= read_multi_range_fetch_next()) != 0) + { + *range_info= multi_range_get_custom(multi_range_buffer, + expected_range_no); + first_running_range++; + m_multi_range_result_ptr= + multi_range_next_entry(m_multi_range_result_ptr, + table_share->reclength); + DBUG_RETURN(res); + } + } + if (!m_next_row) + { + /* + The whole scan is done, and the cursor has been closed. + So nothing more for this range. Move to next. + */ + break; + } + else + { + int current_range_no= m_current_range_no; + /* + For a sorted index scan, we will receive rows in increasing + range_no order, so we can return ranges in order, pausing when + range_no indicate that the currently processed range + (first_running_range) is done. + + But for unsorted scan, we may receive a high range_no from one + fragment followed by a low range_no from another fragment. So we + need to process all index scan ranges together. + */ + if (!mrr_is_output_sorted || expected_range_no == current_range_no) + { + *range_info= multi_range_get_custom(multi_range_buffer, + current_range_no); + /* Copy out data from the new row. */ + unpack_record(table->record[0], m_next_row); + table->status= 0; + /* + Mark that we have used this row, so we need to fetch a new + one on the next call. + */ + m_next_row= 0; + /* + Set m_active_cursor; it is used as a flag in update_row() / + delete_row() to know whether the current tuple is from a scan or + pk operation. + */ + m_active_cursor= m_multi_cursor; + + DBUG_RETURN(0); + } + else if (current_range_no > expected_range_no) + { + /* Nothing more in scan for this range. Move to next. */ + break; + } + else + { + /* + Should not happen. Ranges should be returned from NDB API in + the order we requested them. + */ + DBUG_ASSERT(0); + break; // Attempt to carry on + } + } + + default: + DBUG_ASSERT(0); + } + /* At this point the current range is done, proceed to next. */ + first_running_range++; + m_multi_range_result_ptr= + multi_range_next_entry(m_multi_range_result_ptr, table_share->reclength); + } + + if (first_running_range == ranges_in_seq) + DBUG_RETURN(HA_ERR_END_OF_FILE); + + /* + Read remaining ranges + */ + if ((res= multi_range_start_retrievals(first_running_range))) + DBUG_RETURN(res); + + } +} + +#else // not 'NDB_WITH_NEW_MRR_INTERFACE' + /* This is used to check if an ordered index scan is needed for a range in a multi range read. @@ -15145,6 +16047,7 @@ ha_ndbcluster::read_multi_range_next(KEY multi_range_sorted, multi_range_buffer)); } +#endif /* Fetch next row from the ordered index cursor in multi range scan. @@ -15213,7 +16116,6 @@ ha_ndbcluster::read_multi_range_fetch_ne } DBUG_RETURN(0); } -#endif #ifndef NDB_WITHOUT_JOIN_PUSHDOWN === modified file 'sql/ha_ndbcluster.h' --- a/sql/ha_ndbcluster.h 2011-10-05 07:24:39 +0000 +++ b/sql/ha_ndbcluster.h 2011-10-18 10:43:35 +0000 @@ -345,19 +345,28 @@ class ha_ndbcluster: public handler uchar* buf); int read_range_next(); -#ifndef NDB_WITH_NEW_MRR_INTERFACE /** - * Multi range stuff + * Multi Range Read interface */ - int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, - KEY_MULTI_RANGE*ranges, uint range_count, - bool sorted, HANDLER_BUFFER *buffer); - int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); - bool null_value_index_search(KEY_MULTI_RANGE *ranges, - KEY_MULTI_RANGE *end_range, - HANDLER_BUFFER *buffer); -#endif + int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param, + uint n_ranges, uint mode, HANDLER_BUFFER *buf); + int multi_range_read_next(char **range_info); + ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, + void *seq_init_param, + uint n_ranges, uint *bufsz, + uint *flags, COST_VECT *cost); + ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys, + uint *bufsz, uint *flags, COST_VECT *cost); +private: + uint first_running_range; + uint first_range_in_batch; + uint first_unstarted_range; + /* TRUE <=> need range association */ + bool mrr_need_range_assoc; + + int multi_range_start_retrievals(uint first_range); +public: bool get_error_message(int error, String *buf); ha_rows records(); ha_rows estimate_rows_upper_bound() @@ -806,7 +815,8 @@ private: }; /* For read_multi_range scans, the get_range_no() of current row. */ int m_current_range_no; - + /* For multi range read, return from last mrr_funcs.next() call. */ + int m_range_res; MY_BITMAP **m_key_fields; // NdbRecAttr has no reference to blob NdbValue m_value[NDB_MAX_ATTRIBUTES_IN_TABLE]; @@ -860,14 +870,7 @@ private: ha_ndbcluster_cond *m_cond; bool m_disable_multi_read; - const uchar *m_multi_range_result_ptr; - KEY_MULTI_RANGE *m_multi_ranges; - /* - Points 1 past the end of last multi range operation currently being - executed, to support splitting large multi range reands into manageable - pieces. - */ - KEY_MULTI_RANGE *m_multi_range_defined_end; + uchar *m_multi_range_result_ptr; NdbIndexScanOperation *m_multi_cursor; Ndb *get_ndb(THD *thd); === modified file 'sql/handler.cc' --- a/sql/handler.cc 2011-10-05 07:24:39 +0000 +++ b/sql/handler.cc 2011-10-18 10:43:35 +0000 @@ -3322,8 +3322,6 @@ err: */ uint handler::get_dup_key(int error) { - DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || - m_lock_type != F_UNLCK); DBUG_ENTER("handler::get_dup_key"); #ifndef MCP_BUG59948 if (table == NULL || table->file == NULL) @@ -3338,6 +3336,8 @@ uint handler::get_dup_key(int error) DBUG_RETURN(-1); } #endif + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); table->file->errkey = (uint) -1; if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE || error == HA_ERR_NULL_IN_SPATIAL || === modified file 'storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp' --- a/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp 2011-09-29 09:23:04 +0000 +++ b/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp 2011-10-18 10:43:35 +0000 @@ -235,6 +235,14 @@ public: Uint32 sizeOfPartInfo= 0); /** + * Return size of data, in 32-bit words, that will be send to data nodes for + * all bounds added so far with setBound(). + * + * This method is only available for NdbRecord index scans. + */ + int getCurrentKeySize(); + + /** * Is current scan sorted? */ bool getSorted() const { return m_ordered; } === modified file 'storage/ndb/src/ndbapi/NdbScanOperation.cpp' --- a/storage/ndb/src/ndbapi/NdbScanOperation.cpp 2011-05-20 05:54:20 +0000 +++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp 2011-10-18 10:43:35 +0000 @@ -3445,6 +3445,20 @@ NdbIndexScanOperation::insert_open_bound return 0; } + +int +NdbIndexScanOperation::getCurrentKeySize() +{ + if (unlikely((theStatus != NdbOperation::UseNdbRecord))) + { + setErrorCodeAbort(4284); + /* Cannot mix NdbRecAttr and NdbRecord methods in one operation */ + return -1; + } + return theTupKeyLen; +} + + /* IndexScan readTuples - part of old scan API * This call does the minimum amount of validation and state * storage possible. Most of the scan initialisation is done No bundle (reason: useless for push emails).